June 2002 Programmer's Challenge
Matchsticks
Mail solutions to:
progchallenge@mactech.com
Due Date: 11:59pm ET, Saturday, 8 June 2002
Some time ago, Robin Landsbert sent me a suggestion for a Challenge based on a game he called Nim. In Robin’s version of the game, matchsticks were arranged in rows forming a triangle, one matchstick in the top row, two in the next, three in the next, etc. Two players take turns removing one or more matchsticks from any single row of the board. The object is to make your opponent take the last matchstick.
A little research suggests that this version of the game might not be very difficult. So, in the tradition of the Challenge, we will add a few twists that might make the game (and the Challenge) more interesting. First, we will arrange our matchsticks in a square grid instead of a triangular one, and allow players to remove matchsticks from either a single row or a single column on a given turn. Second, we will not put a matchstick in every position in the grid, leaving a small number of positions empty, perhaps on the order of 10%. Third, we will restrict a player’s moves to removing matchsticks with no intervening holes. That is, a player can remove the n+1 matchsticks in row r located in columns c through c+n only if a matchstick is present in each of those locations. And finally, we will play two versions of the game, one where the player taking the last matchstick loses the game, and one where the player taking the last one wins the game.
The prototype for the code you should write is:
void InitMatchsticks(
short dimension,
/* game is played on a square board of size dimension x dimension */
const char *board,
/* board[row*dimension + col] is board cell (row,col) */
/* board[]==1 represents a matchstick, ==0 represents an empty cell */
bool playFirst,
/* true if you will play first in this game */
bool lastMatchstickLoses,
/* true if taking the last matchstick loses the game,
false if taking the last one wins the game */
short opponent
/* identifier for your opponent in this game */
);
void OpponentMove(
bool playingRow,
/* true if opponent played along a row, false if along a column */
short rowOrColumnNumber,
/* number of the (origin zero) row (playingRow==true) or
column (playingRow==false)
that the opponent played */
short startingColOrRow,
short endingColOrRow,
/* if playingRow==true, the opponent played from
(row,col)==(rowOrColumnNumber,startingColOrRow)
to (row,col)==(rowOrColumnNumber,endingColOrRow)
if playingRow==false, the opponent played from
(row,col)==(startingColOrRow,rowOrColumnNumber)
to (row,col)==(endingColOrRow,rowOrColumnNumber)
*/
const char *board
/* board after your opponent's move */
);
const char *YourMove(
bool *playingRow,
/* true if you played along a row, false if along a column */
short *rowOrColumnNumber,
/* number of the (origin zero) row (playingRow==true) or
column (playingRow==false)
that you played */
short *startingColOrRow,
short *endingColOrRow
/* if *playingRow==true, you played from
(row,col)==(*rowOrColumnNumber,*startingColOrRow)
to (row,col)==(*rowOrColumnNumber,*endingColOrRow)
if *playingRow==false, you played from
(row,col)==(*startingColOrRow,*rowOrColumnNumber)
to (row,col)==(*endingColOrRow,*rowOrColumnNumber)
*/
/* return value is a pointer to a board after your move */
);
void TermMatchsticks();
The objective of the Challenge is to win as many games as possible against your fellow contestants, while expending as little execution time as possible. Each game begins with a call to your InitMatchsticks routine, where you are given the dimension of the game, the initial board configuration, the identity of your opponent, whether or not you playFirst, and whether the objective is to take or not take the last matchstick (lastMatchstickLoses). When it is your turn to move, your YourMove routine describes the move you are making (playingRow, rowOrColumnNumber, startingColOrRow, endingColOrRow) and returns a pointer to your view of what the board looks like after your move. When your opponent moves, your OpponentMove routine is provided with a description of the opponent’s move, and the board configuration after that move.
The Challenge will be scored as a round robin tournament, or another fair scheduling mechanism. Each player will play first and play second against each scheduled opponent an equal number of times for each test case. Each player will play to win by taking the last matchstick, and play to win by making the opponent take the last matchstick, an equal number of times against each scheduled opponent for each test case. A player’s score will be dimension^2 points for each win, minus a penalty of 10 points per millisecond of execution time. You can earn a bonus of up to 25% of your score based on a subjective evaluation of the clarity of your code and commentary.
This will be a native PowerPC Carbon Challenge, using the Metrowerks CodeWarrior Pro 7.0 development environment. Please be certain that your code is carbonized, as I may evaluate this Challenge using Mac OS X. Unfortunately, this Challenge cannot accommodate alternative development environments, because pairs of solutions need to compete against one another in a single executable.
Test code for this Challenge is available.
You can get a head start on the Challenge by reading the
Programmer's Challenge mailing list. It will be posted to the list on
or before the 12th of the preceding month. To join, send an email to
listserv@listmail.xplain.com
with the subject "subscribe challenge-A". You can also join the
discussion list by sending a message with the subject "subscribe
challenge-D".
|