-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
House rules #5
Comments
Makes sense to add Also, we don't need to stick to the existing events only, we could add Actually, I was thinking of something completely based on events. The engine itself would only know the basic, official rules. But in every key point, it would emit an event, like House rules code (that would be somehow like plugins) would attach to the necessary events and modify game flow. The engine would allow these plugins to avoid some actions, like if we The API to load different house rules could be:
As an example:
I'm just throwing ideas here, open to any other solution. Interesting links:
|
It would definitely be a good idea to send the game instance to each event. Users might be catching the events in different files, and that would help greatly so they don't have to manually pass it around. My only concern is that the events are tied to the //main.js
const { Game } = require("uno-engine");
//- you would have to create the game first
let game = Game(players);
//- and then you could load the eventloader
require("./eventLoader.js")(game);
//- if you want to start a new game with different players, you have to reload the eventloader
//- not great if the eventloader is managing events for all modules in your project
//eventLoader.js
const reqEvent = (event) => require(`./events/${event}`);
module.exports = (game) => {
//eventloader needs `game` to be able to use `game.on()`
//you already have `game` in higher scope, and can pass it into each event yourself
game.on("cardplay", (err, playCard, playBy) => reqEvent("cardplay")(game, err, playCard, playBy));
game.on("nextplayer", (err, nextPlayer) => reqEvent("nextplayer")(game, err, nextPlayer));
game.on("end", (err, winner, score) => reqEvent("end")(game, err, winner, score));
};
//events/cardplay.js
module.exports = (game, err, playCard, playBy) => {
//cardplay event code
}; With the events being on //main.js
const { Game } = require("uno-engine");
require("./eventLoader.js")(Game);
let game = Game(players);
//eventLoader.js
const reqEvent = (event) => require(`./events/${event}`);
module.exports = (Game) => {
//and if the library passed `game` instance into events
Game.on("cardplay", (game, err, playCard, playBy) => reqEvent("cardplay")(game, err, playCard, playBy));
Game.on("nextplayer", (game, err, nextPlayer) => reqEvent("nextplayer")(game, err, nextPlayer));
Game.on("end", (game, err, winner, score) => reqEvent("end")(game, err, winner, score));
};
//events/cardplay.js
module.exports = (game, err, playCard, playBy) => {
//cardplay event code
};
import { Game } from 'uno-engine';
import { CumulativeDrawTwo } from 'uno-engine/cumulative-draw-two'; // maintained house rule
import { CumulativeDrawWildFour } from 'uno-engine-cumulative-draw-wild-four'; // community house rule
const players = ["Player 1", "Player 2"];
const game = Game(players, [CumulativeDrawTwo, CumulativeDrawWildFour]); That looks like a much better solution than what I had in mind. I hadn't even considered something like that. That would also allow for, as you noted, others to make their own rules plugins. |
I think this eventloader pattern is a lot confusing - never seen it. Do you have any resource about it? Also, it doesn't make much sense to emit events from/register listeners to the constructor (factory, actually - Events are actions that happened in one specific instance. I believe making the
Multiple games should not be the concern of this engine. It is responsible for managing a single Uno game. If you (aka the user) want to have a lot of games, it's up to you to manage that. You could create a factory function to create games tailored for your need:
This can be solved using the idea of the snippet above. We can't cover every single use case in here, we should keep the engine simple, doing one thing, and doing it well: managing a single Uno gameplay. |
There isn't really any specific documentation I can find on event loaders/handlers, but it is common practice with large projects to split each event out into their own files for clarity, especially when you have many module dependencies that all have their own events. It gets cluttered in the main file otherwise. The loader itself just requires the needed event file when an event is emitted. A more verbose eventLoader.js would look something like this const reqEvent = function(eventName) {
return require(`./events/${eventName}`);
};
const loadEvents = function(game) {
game.on("cardplay", function(err, playedCard, playedBy) {
const cardplay = reqEvent("cardplay"); //same as `require("./events/cardplay.js")`
return cardplay(game, err, playedCard, playedBy); //injecting `game` when calling cardplay.js
});
game.on("nextplayer", function(err, nextPlayer) {
const nextplayer = reqEvent("nextplayer");
return nextplayer(game, err, nextPlayer);
});
game.on("end", function(err, winner, score) {
const end = reqEvent("end");
return end(game, err, winner, score);
});
};
module.exports = loadEvents; This is the same as const reqEvent = (event) => require(`./events/${event}`);
module.exports = (game) => {
game.on("cardplay", (err, playCard, playBy) => reqEvent("cardplay")(game, err, playCard, playBy));
game.on("nextplayer", (err, nextPlayer) => reqEvent("nextplayer")(game, err, nextPlayer));
game.on("end", (err, winner, score) => reqEvent("end")(game, err, winner, score));
};
The problem with that snippet is that it assumes that the uno events are the only ones being used in the eventloader. That would work if you use a separate eventloader for each module, but the purpose of an eventloader is to load all events for all of your required modules, and dynamically require the correct file for each event when that event is called. Eventloaders have many arguments passed into them, each argument being for a different module's event handler, like this: //main.js
require("./eventLoader.js")(chatbot, twitter, etc);
//and each of those has it's own set of events that get loaded
//eventLoader.js
module.exports = (chatbot, twitter, etc) => {
chatbot.on("message", (msg) => reqEvent("message")(chatbot, msg));
twitter.on("tweet", (tweet) => reqEvent("tweet")(tweet));
//etc.
}; I think it may just make more sense for users to keep the events all in the same file as when the Edit: Maybe it would make more sense if the players weren't passed into the game so early. They could be passed into the game instance when starting a new game, instead of when creating the game instance. const { Game } = require("uno-engine");
const game = Game();
game.newGame(players); Then the game instance never has to be overwritten, and you can just start new games with different sets of players instead of redeclaring the game instance every time. |
I implemented a draft of how house rules would be implemented and used in the engine on PR #12. The first step was to create a kind of cancelable EventEmitter, so house rules implementors may ask the engine to stop its common flow. If the event handler returns Then we can setup our plugin to listen to specific events and take over whenever they need. The code needs improvements, so I won't merge the PR right away, but the general idea is there. |
As per the discussion that started in #2, giving users the ability to create their own rules with the usage of events and/or options.
Copied:
Maybe repurpose the
cardplay
event, so that it fires when a card is played but before it gets placed in the discard pile (and include both the card played and the current discard card in the arguments), so anyone using the lib has the ability to intercept card plays and inject their own house rules before each card gets placed. You already have thenextplayer
event, which fires directly aftercardplay
and could include the newly placed card in the arguments, to (mostly) replace the need for the currentcardplay
event.This would probably require some additional lib functionality as well so that you would have the ability to prevent certain lib actions like automatically moving to the next player or automatically drawing cards. This is where I think an
options
object could work well - pass in an object with the default/official rules that you want to turn off and handle manually. This would require the ability to actually do those things manually though, for example, giving the lib user the ability to draw cards into any player's hand without anything added/extra occurring like moving to the next player (i.e. if they wanted to start with 10 cards each instead of 7).Maybe it would make sense to add
player.play()
andplayer.draw()
so that the user has more control over who is playing or drawing if they want to manually do the player switching. You can also keepgame.play()
andgame.draw()
as functions to just use the default official rules, and havegame.goToNextPlayer()
be public, to manually move to the next player (maybe you don't want to require draw before pass).The text was updated successfully, but these errors were encountered: