(We recommend that you write your code in a separate text editor, then copy-paste it here.)
(This does not submit your player to the tournament. There are further directions below for that.)
Here is an example that finds the first domino of the correct color and knocks it to the right:
function userGetMove(playerId, position) {
for (var i = 0; i < position.rows.length; i++) {
const row = position.rows[i];
for (var j = 0; j < row.length; j++) {
const dominoColor = row[j];
if (dominoColor == playerId) {
//this domino is my color
//knock it to the right
row.splice(j, row.length - j + 1);
return position;
}
}
}
}
Toppling Dominoes is a partizan combinatorial game played on rows of blue and red dominoes. On their turn, the current player (Blue or Red) chooses one of their dominoes in any row. They then choose to tip it to the right or left. The domino knocks over all other dominoes in its path and all of them are removed from the game. You can play Toppling Dominoes here.
We are holding a computer player tournament as part of Sprouts 2025. People can use this to test their players. Instructions to submit a player are below.
You can work in a group or on your own.
We are planning to move forward with boards with 8 rows, each with a maximum of 10 dominoes per row. I am planning 15 games per match.
We'll need players to run efficiently. For the actual conference tournament, if we run this with a bunch of contestants at the same time as we're running Zoom, it will get bogged down quickly. (And Kyle's laptop is not very powerful.) Please make sure your player takes their turn in less than 6 seconds on our starting positions on your own machine. (If your machine isn't too overpowered, that should equate to about 15 seconds on my laptop.) If specific players are running too long, we'll have to exclude them from the tournament. (If you disagree with these rules, feel free to talk to me. We would much rather have more players than fewer!)
Below, you'll see the details for submitting your player class. It does not have to be "stateless"; you can definitely include fields that your player uses to make moves.
Check out the instructions below. (After this EFAQ.)
Yes, those directions are below!
Keep watching this space, or watch @CGTKyle@mathstodon.xyz (Mastodon) for updates.
Oh yeah. I got it working, but I definitely need to clean it up. Please don't tell my software engineering students! I'll refactor it when I have time (please don't check to see if this answer is the same as it was last year).
If you get a player working as above, you'll need to make a few changes to get it working for the actual Sprouts tournament.
//author: <Your name or your team's name>, yourcontactemail@example.com
var <YourPlayerName> = Class.create(ComputerPlayer, {
initialize: function() {
//nothing needed here, but you can add things
//if you want a stateful player
},
givePosition: function(playerIndex, position, referee) {
//don't modify this method.
referee.moveTo(this.userGetMove(playerIndex, position));
},
userGetMove: function(playerId, position) {
//paste your code in here.
},
getName: function() {
return "<YourPlayerName>";
},
getAuthor: function() {
return "<Your name or your team's name>";
}
});
paithanq
@gmail.com
) so we'll be notified of your late submission. If we are able to, we'll include your player! (Most years so far we are low on players and would be excited to get more.)By popular demand, we've included a way to pit two (or more) players against each other.
Here is the underlying JavaScript code for the Toppling Dominoes class, which uses the prototype package to define objects. It is currently a part of the (very large) combinatorialGames.js file I maintain.
/** * Toppling Dominoes game * * Grid is stored as a 2D array of single rows. Each row is an array of integers where each represents the color of one domino. In order to keep things looking neat, we leave in the space where fallen dominoes were; thus we also include empty dominoes in the arrays, which are unplayable. * @author Kyle Burke. */ const TopplingDominoes = Class.create(CombinatorialGame, { /** * Constructor. * */ initialize: function(numRows, minDominoesInRow, maxDominoesInRow) { this.playerNames = ["Blue", "Red"]; this.rows = []; //default probability const colors = [CombinatorialGame.prototype.LEFT, CombinatorialGame.prototype.RIGHT]; var numBlueEnds = 0; var numRedEnds = 0; for (var i = 0; i < numRows; i++) { const row = []; const dominoDifference = maxDominoesInRow - minDominoesInRow; const numDominoes = Math.floor(Math.random() * dominoDifference) + minDominoesInRow; for (var j = 0; j < numDominoes; j++) { row.push(randomChoice(colors)); } this.rows.push(row); if (row[0] == CombinatorialGame.prototype.LEFT) { numBlueEnds ++; if (numBlueEnds > numRows) { //we have too many blue dominoes on the ends and need to make this one red instead row[0] = CombinatorialGame.prototype.RIGHT; numRedEnds++; numBlueEnds--; } } else { numRedEnds ++; if (numRedEnds > numRows) { //we have too many red dominoes on the ends and need to make this one blue instead row[0] = CombinatorialGame.prototype.LEFT; numRedEnds--; numBlueEnds++; } } if (row[row.length-1] == CombinatorialGame.prototype.LEFT) { numBlueEnds++; if (numBlueEnds > numRows) { //we have too many blue dominoes on the ends and need to make this one red instead row[row.length-1] = CombinatorialGame.prototype.RIGHT; numRedEnds++; numBlueEnds--; } } else { numRedEnds++; if (numRedEnds > numRows) { //we have too many red dominoes on the ends and need to make this one blue instead row[row.length-1] = CombinatorialGame.prototype.LEFT; numRedEnds--; numBlueEnds++; } } } } /** * Returns the width of this board. */ ,getMaxNumDominoes: function() { var maxLength = 0; for (const row of this.rows) { if (row.length > maxLength) { maxLength = row.length; } } return maxLength; } /** * Returns the height of this board. */ ,getNumRows: function() { return this.rows.length; } /** * Equals! */ ,equals: function(other) { return omniEquals(this.rows, other.rows); } /** * Clone. */ ,clone: function() { const copy = new TopplingDominoes(1, 1, 1); copy.rows = omniClone(this.rows); return copy; } /** * Gets the options. */ ,getOptionsForPlayer: function(playerId) { const options = []; for (var i = 0; i < this.rows.length; i++) { const row = this.rows[i]; for (var j = 0; j < row.length; j++) { if (row[j] == playerId) { options.push(this.getLeftPushOption(i, j)); options.push(this.getRightPushOption(i, j)); } } } return options; } /** * Gets an option by knocking a domino to the left. */ ,getLeftPushOption: function(rowIndex, dominoIndex) { const option = this.clone(); //option.rows[rowIndex].splice(0, dominoIndex+1); for (var i = 0; i <= dominoIndex; i++) { option.rows[rowIndex][i] = TopplingDominoes.prototype.NO_DOMINO; } return option; } /** * Gets an option by knocking a domino to the right. */ ,getRightPushOption: function(rowIndex, dominoIndex) { const option = this.clone(); //option.rows[rowIndex].splice(dominoIndex, option.rows[rowIndex].length - dominoIndex); for (var i = dominoIndex; i < option.rows[rowIndex].length; i++) { option.rows[rowIndex][i] = TopplingDominoes.prototype.NO_DOMINO; } return option; } }); // end of TopplingDominoes TopplingDominoes.prototype.PLAYER_NAMES = ["Blue", "Red"];