Code:
/*
* ****************************************************************************
* RP6 ROBOT SYSTEM - RP6 CONTROL M32 TESTS
* ****************************************************************************
* Example: Kalaha
* Author(s): Dirk
* ****************************************************************************
* Description:
* This program for RP6 Control plays the old game Kalaha, invented by William
* Julius Champion Jr in 1940.
*
* Rules:
* Kalaha is usually played on a board with two rows of 6 flat deepenings
* (called field or house) and two more bigger deepenings (called kalah, home
* or store) on the left and right end of the two rows.
* Both players are normally sitting opposite. The 12 fields in two rows are
* filled with 3 seeds or other small things (like beans, stones ...). Each
* player controls the six fields next to him and the home field to his right.
*
* Alternately each player decides to take all seeds out of ONE of the 6
* fields of his row. Moving counter-clockwise, he drops one seed in each
* field in turn, including the player's own home but not his opponent's.
* If the last seed is sawn into the player's own home, he gets to go again.
* There is no limit on the number of moves a player can make in his turn.
* If the last seed is dropped into an empty field owned by the player, he
* gains the last seed and all of the seeds in the field directly opposite
* the one he ended in, and all of those seeds are moved into his home. When
* one side runs out of seeds, the other player moves all seeds from his
* fields into his home and whoever has the most in his home wins.
*
* Variations:
* - Starting the game with four, five or six seeds in each field.
* - The "Empty Capture" variant modifies the rules to prohibit a player from
* capturing seeds from his opponent when landing in an empty field - i.e.,
* only the last dropped seed is placed into the home.
* - Not counting the remaining seeds as part of the opponent's score at the
* end of the game.
*
* You will find further information here:
* http://en.wikipedia.org/wiki/Kalah
*
* ############################################################################
* The Robot does NOT move in this example! You can simply put it on a table
* next to your PC and you should connect it to the PC via the USB Interface!
* ############################################################################
* ****************************************************************************
*/
/*****************************************************************************/
// Includes:
#include "RP6ControlLib.h" // The RP6 Control Library (1.3beta or higher).
// Always needs to be included!
/*****************************************************************************/
// Defines:
// Number of fields (Default: 6, Range: 2..11):
#define MAXFIELDS 6
// Seeds in each field (Default: 3, Range: 2..11):
#define MAXSEEDS 3
// 4 seeds in each field:
//#define MAXSEEDS 4
// 5 seeds in each field:
//#define MAXSEEDS 5
// 6 seeds in each field:
//#define MAXSEEDS 6
// Show rules:
#define SHOW_RULES
// -------------------------------------------------------
// Highest index of field[]:
#define MAXINDEX (MAXFIELDS * 2 + 1)
// Index of home field 1:
#define KALAH1 MAXFIELDS
// Index of home field 2:
#define KALAH2 MAXINDEX
// Number of seeds / 2 (Tie):
#define TIE (MAXSEEDS * MAXFIELDS)
// Constants for gamemode:
#define PLAYER_PLAYER 0
#define COMPUTER_PLAYER 1
#define COMPUTER_COMPUTER 2
// Output delay (ms):
#define WAIT_MS 1000
#define WAITDEMO_MS 2000
// -------------------------------------------------------
// Variations:
// "Empty Capture" variation:
//#define EMPTY_CAPTURE
// Don't count the opponent's seeds at the end of the game:
//#define REMAINING_SEEDS
/*****************************************************************************/
// Variables:
// Reception buffer for the function getInputLine():
char receiveBuffer[UART_RECEIVE_BUFFER_SIZE + 1];
uint8_t field[MAXINDEX + 1];
uint8_t firstmove;
uint16_t pause_ms;
uint8_t gamemode;
uint8_t turn;
uint8_t move;
uint8_t winner;
uint8_t winner1;
uint8_t winner2;
/*****************************************************************************/
// Functions:
// UART receive functions:
/**
* Get chars of an input line from the UART.
*
* Returns 0 (false), if the UART receive buffer is empty
* OR a character of the input line has been received.
* Returns 1, if the whole input line has been received
* (with a "new line" character at the end).
* Returns 2, if the UART receive buffer overflows.
* The input line is stored in the receiveBuffer array.
*
*/
uint8_t getInputLine(void)
{static uint8_t buffer_pos = 0;
if(getBufferLength()) {
receiveBuffer[buffer_pos] = readChar();
if(receiveBuffer[buffer_pos] == '\n') {
receiveBuffer[buffer_pos] = '\0';
buffer_pos = 0;
return 1;
}
else if(buffer_pos >= UART_RECEIVE_BUFFER_SIZE) {
receiveBuffer[UART_RECEIVE_BUFFER_SIZE] = '\0';
buffer_pos = 0;
return 2;
}
buffer_pos++;
}
return 0;
}
/**
* Get a complete input line from the UART.
*
* This function waits for a whole input line from the UART.
* The input line is stored in the receiveBuffer array.
* The function is blocking until one of the two following
* conditions occurs:
* - A "new line" character has been received at the end of
* the input line.
* - The UART receive buffer overflows.
*
*/
void enterString(void)
{
while(!getInputLine());
}
/**
* GET SEED
*
* Gets a starting value for srand().
*
*/
uint16_t get_seed(void)
{
uint16_t seed = 0;
uint16_t *p = (uint16_t*) (RAMEND + 1);
extern uint16_t __heap_start;
while (p >= &__heap_start + 1)
seed ^= * (--p);
return seed;
}
/**
* WRITE 6 SPACES
*
* Sends 6 spaces for each field via UART (used by showBoard).
*
*/
void write6Spaces(void)
{
uint8_t i;
for (i = 0; i < KALAH1; i++) {
writeString_P(" ");
}
}
/**
* SHOW BOARD
*
* Shows the Kalaha board in field[] on the terminal.
*
*/
void showBoard(void)
{
uint8_t i;
writeString_P("\n -2- ");
for (i = KALAH1; i > 0; i--) {
writeString_P("[F");
writeIntegerLength(i, DEC, 2); // Player 2 fields
writeString_P("] ");
}
writeString_P(" -2-\n");
writeString_P(" < ");
for (i = (KALAH2 - 1); i > KALAH1; i--) {
writeIntegerLength(field[i], DEC, 3); // Player 2 side
writeString_P(" < ");
}
writeChar('\n');
writeString_P(" v ");
write6Spaces();
writeString_P("^\n");
writeString_P(" v ");
write6Spaces();
writeString_P(" ^\n");
writeString_P(" ");
writeIntegerLength(field[KALAH2], DEC, 3); // Player 2 result
writeString_P(" ");
write6Spaces();
writeIntegerLength(field[KALAH1], DEC, 3); // Player 1 result
writeString_P("\n v ");
write6Spaces();
writeString_P(" ^\n");
writeString_P(" v ");
write6Spaces();
writeString_P("^\n");
writeString_P(" > ");
for (i = 0; i < KALAH1; i++) {
writeIntegerLength(field[i], DEC, 3); // Player 1 side
writeString_P(" > ");
}
writeChar('\n');
writeString_P(" -1- ");
for (i = 1; i <= KALAH1; i++) {
writeString_P("[F");
writeIntegerLength(i, DEC, 2); // Player 1 fields
writeString_P("] ");
}
writeString_P(" -1-\n");
}
/**
* INIT FIELD ARRAY
*
* Initialises the field array.
*
*/
void initFieldArray(void)
{
uint8_t i;
for (i = 0; i <= MAXINDEX; i++) {
field[i] = MAXSEEDS;
}
field[KALAH1] = 0; // Result field 1
field[KALAH2] = 0; // Result field 2
}
/**
* CALCULATE MOVE
*
* Calculates each move. The variable move contains the actual
* field index to be processed in field[]. After the calculation
* field[] contains the new field (board). The variable turn
* determines the next player (1/2) or the victory condition (0).
*
*/
void calculateMove(void)
{
uint8_t actualmove;
uint8_t resultfield;
uint8_t I;
uint8_t fieldindex;
#ifndef REMAINING_SEEDS
uint8_t seeds;
#endif
// Calculate a move:
actualmove = move;
if ((turn == 0) || (field[actualmove] == 0)) return;
if (turn == 1) resultfield = KALAH1;
if (turn == 2) resultfield = KALAH2;
fieldindex = actualmove;
do {
actualmove += 1;
if (actualmove > MAXINDEX) actualmove = 0;
// Jump over the opponent's result field:
if ((actualmove == KALAH1) && (resultfield != KALAH1)) {
actualmove += 1;
}
if ((actualmove == KALAH2) && (resultfield != KALAH2)) {
actualmove = 0;
}
field[actualmove] += 1;
field[fieldindex] -= 1;
} while (field[fieldindex] > 0);
// Calculate the "special case":
// Special case: Last seed drops into an empty field of the actual
// player and the opposite field is not empty
if (((resultfield == KALAH1) && (actualmove < KALAH1))
|| ((resultfield == KALAH2) && (actualmove > KALAH1))) {
if ((field[actualmove] == 1) && (actualmove != KALAH2)
&& (field[MAXINDEX - 1 - actualmove] > 0)) {
field[actualmove] = 0;
field[resultfield] += 1;
#ifndef EMPTY_CAPTURE
field[resultfield] += field[MAXINDEX - 1 - actualmove];
field[MAXINDEX - 1 - actualmove] = 0;
#endif
}
}
// Test the victory conditions:
// Continue playing, if own fields are not empty:
fieldindex = 0;
#ifndef REMAINING_SEEDS
seeds = 0;
#endif
for (I = 0; I < MAXFIELDS; I++) {
if (field[resultfield - KALAH1 + I] > 0) fieldindex = 1;
#ifndef REMAINING_SEEDS
seeds += field[KALAH2 - resultfield + I];
#endif
}
if (fieldindex == 1) {
if (actualmove == resultfield) return; // Once more (extra move)
}
else {
turn = 0; // No more seeds: End of game
#ifndef REMAINING_SEEDS
field[KALAH1 + KALAH2 - resultfield] += seeds;
for (I = 0; I < MAXFIELDS; I++) {
field[KALAH2 - resultfield + I] = 0;
}
#endif
return;
}
// Change player, if his fields are not empty:
fieldindex = 0;
#ifndef REMAINING_SEEDS
seeds = 0;
#endif
for (I = 0; I < MAXFIELDS; I++) {
if (field[KALAH2 - resultfield + I] > 0) fieldindex = 1;
#ifndef REMAINING_SEEDS
seeds += field[resultfield - KALAH1 + I];
#endif
}
if (fieldindex == 1) {
turn = 3 - turn; // Change player
}
else {
turn = 0; // No more seeds: End of game
#ifndef REMAINING_SEEDS
field[KALAH1 + KALAH2 - resultfield] += seeds;
for (I = 0; I < MAXFIELDS; I++) {
field[resultfield - KALAH1 + I] = 0;
}
#endif
}
}
/**
* SHOW MOVE
*
* Shows the actual move (board) on the terminal and displays
* the victory message at the end of a game.
*
*/
void showMove(void)
{
mSleep(1000);
showBoard(); // Show fields
if (turn != 0) {
mSleep(pause_ms); // Pause the output for (pause_ms) ms
return;
}
if (field[KALAH1] > field[KALAH2]) {
writeString_P("\nSpieler/Computer 1 hat gewonnen!");
winner = 1;
winner1++;
}
if (field[KALAH1] < field[KALAH2]) {
writeString_P("\nSpieler/Computer 2 hat gewonnen!");
winner = 2;
winner2++;
}
if (field[KALAH1] == field[KALAH2]) {
writeString_P("\nUnentschieden!");
winner = 0;
}
writeString_P(" Spielende.\n");
writeString_P("\nSpielstand: ");
writeInteger(winner1, DEC);
writeString_P(" : ");
writeInteger(winner2, DEC);
writeString_P(" !\n");
}
/**
* COMPUTER 1 MOVE
*
* Calculates the computer 1 move in the actual field[].
* After processing the variable move contains the field
* index of the new computer 1 move. This function calls
* calculateMove() and displays a text informing about
* the move.
*
*/
void computer1Move(void)
{
uint8_t I, J;uint8_t movefield;uint8_t fieldindex;do {I = rand() % KALAH1;
} while (field[I] == 0);movefield = I;for (I = (MAXINDEX - 2); I > KALAH1;
I--) {if (field[I] > 0) {fieldindex = I + field[I];if (fieldindex >= KALAH2)
continue;if (field[fieldindex] == 0) {if (field[MAXINDEX - 1 - fieldindex] > 0)
{movefield = MAXINDEX - 1 - fieldindex; break;}}}}for (I = KALAH1; I > 0; I--)
{J = I - 1; if (field[J] > 0) {fieldindex = J + field[J];if (fieldindex >
KALAH1) {movefield = J;break;}}}for (I = (KALAH1 - 1); I > 0; I--) {J = I - 1;
if (field[J] > 0) {fieldindex = J + field[J];if (fieldindex >= KALAH1)
continue;if (field[fieldindex] == 0) {if (field[MAXINDEX - 1 - fieldindex] > 0)
{movefield = J;break;}}}}for (I = KALAH1; I > 0; I--) {J = I - 1;if (field[J]
> 0) {fieldindex = J + field[J];if (fieldindex == KALAH1) {movefield = J;
break;}}}move = movefield;calculateMove();writeString_P("\nComputer 1: Zieht Feld ");
writeInteger((move + 1), DEC);writeString_P(".\n");}
/**
* COMPUTER 2 MOVE
*
* Calculates the computer 2 move in the actual field[].
* After processing the variable move contains the field
* index of the new computer 2 move. This function calls
* calculateMove() and displays a text informing about
* the move.
*
*/
void computer2Move(void)
{
uint8_t I, J;uint8_t movefield;uint8_t fieldindex;do {I = rand() % KALAH1 +
KALAH1 + 1;} while (field[I] == 0);movefield = I;for (I = (KALAH1 - 1); I > 0;
I--) {J = I - 1;if (field[J] > 0) {fieldindex = J + field[J];if (fieldindex
>= KALAH1) continue;if (field[fieldindex] == 0) {if (field[MAXINDEX - 1 -
fieldindex] > 0) {movefield = MAXINDEX - 1 - fieldindex;break;}}}} for (I =
(MAXINDEX - 1); I > KALAH1; I--) {if (field[I] > 0) {fieldindex = I +
field[I];if (fieldindex > MAXINDEX) {movefield = I;break;}}}for (I =
(MAXINDEX - 2); I > KALAH1; I--) {if (field[I] > 0) {fieldindex = I +
field[I];if (fieldindex >= MAXINDEX) continue;if (field[fieldindex] == 0) {
if (field[MAXINDEX - 1 - fieldindex] > 0) {movefield = I;break;}}}}for (I =
(MAXINDEX - 1); I > KALAH1; I--) {if (field[I] > 0) {fieldindex = I +
field[I];if (fieldindex == MAXINDEX) {movefield = I;break;}}}move = movefield;
calculateMove();writeString_P("\nComputer 2: Zieht Feld ");writeInteger((move
- KALAH1), DEC);writeString_P(".\n");}
/**
* PLAYER 1 MOVE
*
* Player 1 move. The function asks for the move of player 1.
* The player enters a number from 1 to KALAH1 to select one
* of his fields. This function calls calculateMove().
*
*/
void player1Move(void)
{
uint8_t movefield = KALAH1;
writeString_P("\nSpieler 1: Dein Zug (Feld 1..");
writeInteger(movefield, DEC);
writeString_P(")?\n");
do {
enterString();
if (receiveBuffer[1] == '\0') {
movefield = receiveBuffer[0] - 49;
}
else {
if (receiveBuffer[2] == '\0') {
movefield = (receiveBuffer[0] - 48) * 10 + receiveBuffer[1] - 49;
}
}
}
while ((movefield >= KALAH1) || (field[movefield] == 0));
firstmove = 0;
move = movefield; // 0..(KALAH1 - 1)
calculateMove();
}
/**
* PLAYER 2 MOVE
*
* Player 2 move. The function asks for the move of player 2.
* The player enters a number from 1 to KALAH1 to select one
* of his fields. This function calls calculateMove().
*
*/
void player2Move(void)
{
uint8_t movefield = KALAH1;
writeString_P("\nSpieler 2: Dein Zug (Feld 1..");
writeInteger(movefield, DEC);
writeString_P(")?\n");
do {
enterString();
if (receiveBuffer[1] == '\0') {
movefield = receiveBuffer[0] - 48 + KALAH1;
}
else {
if (receiveBuffer[2] == '\0') {
movefield = (receiveBuffer[0] - 48) * 10 + receiveBuffer[1] - 48 + KALAH1;
}
}
}
while ((movefield <= KALAH1) || (movefield >= KALAH2)
|| (field[movefield] == 0));
firstmove = 0;
move = movefield; // (KALAH1 + 1)..(KALAH2 - 1)
calculateMove();
}
/**
* PLAYER V PLAYER
*
* Player 2 against player 1. Function only ends, if turn == 0.
*
*/
void playerVplayer(void)
{
do {
while (turn == 1) {
player1Move();
showMove();
};
while (turn == 2) {
player2Move();
showMove();
};
} while (turn != 0);
}
/**
* COMPUTER V PLAYER
*
* Computer 2 against player 1. Function only ends, if turn == 0.
*
*/
void computerVplayer(void)
{
do {
while (turn == 1) {
player1Move();
showMove();
};
while (turn == 2) {
computer2Move();
showMove();
};
} while (turn != 0);
}
/**
* COMPUTER DEMO
*
* Computer 2 against computer 1. Function only ends, if turn == 0.
*
*/
void computerDemo(void)
{
pause_ms = WAITDEMO_MS;
do {
while (turn == 1) {
computer1Move();
showMove();
};
while (turn == 2) {
computer2Move();
showMove();
};
} while (turn != 0);
pause_ms = WAIT_MS;
}
/**
* NEW GAME
*
* Performs a new Kalaha game. Function only ends, if turn == 0.
*
*/
void newGame(void)
{
// Initialisation:
pause_ms = WAIT_MS;
if (!gamemode) {
gamemode = COMPUTER_PLAYER; // Default mode -> Player : Computer
}
turn = rand() % 2 + 1; // Default: Random player or computer begins or
if (winner) turn = winner; // the last winning player or computer begins
firstmove = 1; // First move of a game
// Menu:
writeString_P("\n\n****** Kalaha Version 1.0 ******\n");
#ifdef SHOW_RULES
writeString_P("Spielregeln:\n");
writeString_P("Das Kalaha-Spielbrett besteht aus zwei Muldenreihen mit\n");
writeString_P("jeweils sechs Spielmulden. Außerdem befindet sich an\n");
writeString_P("jedem Ende eine große Gewinnmulde, auch Kalah genannt,\n");
writeString_P("welche im Spiel die gefangenen Samen aufnimmt.\n");
writeString_P("Jedem Spieler gehören die sechs Spielmulden auf seiner\n");
writeString_P("Seite des Brettes und die rechts von ihm gelegene Ge-\n");
writeString_P("winnmulde.\n");
writeString_P("Vorbereitung: Zu Beginn des Spiels werden alle Spiel-\n");
writeString_P("mulden mit jeweils drei Samen gefüllt. Gewöhnlich fängt\n");
writeString_P("der letzte Sieger das neue Spiel an.\n");
writeString_P("Das Ziel des Spiels ist es, mehr Samen zu sammeln als\n");
writeString_P("der Gegner. Da es nur 36 Samen gibt, reichen 19, um das\n");
writeString_P("zu erreichen. Da es eine gerade Anzahl an Samen gibt,\n");
writeString_P("ist ein Unentschieden möglich, wenn beide Spieler am\n");
writeString_P("Ende 18 Samen ihr Eigen nennen.\n");
writeString_P("Spielrunde: Die beiden Spieler sind abwechselnd am Zug.\n");
writeString_P("Der Spieler entscheidet sich für eine seiner sechs\n");
writeString_P("Spielmulden, nimmt alle Samen heraus und veteilt sie\n");
writeString_P("einzeln gegen den Uhrzeigersinn (links herum) auf die\n");
writeString_P("folgenden Spielmulden einschließlich der eigenen Ge-\n");
writeString_P("winnmulde, aber mit Ausnahme der gegnerischen Gewinn-\n");
writeString_P("mulde.\n");
writeString_P("Fällt der letzte Samen in die eigene Gewinnmulde, be-\n");
writeString_P("kommt der Spieler eine Extra-Runde, d.h. er darf erneut\n");
writeString_P("ziehen. Dies kann auch mehrfach vorkommen.\n");
writeString_P("Fangen: Wenn der letzte Samen in einer leeren Spiel-\n");
writeString_P("mulde des aktiven Spielers landet und direkt gegenüber\n");
writeString_P("in der gegnerischen Mulde Samen liegen, sind sowohl der\n");
writeString_P("letzte Samen als auch die gegenüberliegenden Samen ge-\n");
writeString_P("fangen und werden zu den eigenen Samen in die Gewinn-\n");
writeString_P("mulde gelegt.\n");
writeString_P("Spielende: Wenn ein Spieler an der Reihe ist, jedoch\n");
writeString_P("alle seine Spielmulden leer sind, ist das Spiel be-\n");
writeString_P("endet.\n");
writeString_P("Der Gegner leert seine Spielmulden ebenfalls und legt\n");
writeString_P("die Samen in seine Gewinnmulde. Gewinner ist, wer die\n");
writeString_P("meisten Samen in seiner Gewinnmulde hat.\n\n");
writeString_P("Weitere Informationen kann man hier finden:\n");
writeString_P(" http://de.wikipedia.org/wiki/Kalaha\n");
#endif
writeString_P("\nWelches Spiel willst du spielen?\n");
writeString_P(" -1- Spieler : Spieler\n");
writeString_P(" -2- Spieler : Computer\n");
writeString_P(" -3- Computer : Computer (Demo)\n");
enterString();
if (receiveBuffer[0] == '1') gamemode = PLAYER_PLAYER;
if (receiveBuffer[0] == '2') gamemode = COMPUTER_PLAYER;
if (receiveBuffer[0] == '3') gamemode = COMPUTER_COMPUTER;
writeString_P("\nWer soll anfangen?\n");
writeString_P(" -1- Spieler/Computer 1 (unten)\n");
writeString_P(" -2- Spieler/Computer 2 (oben)\n");
writeString_P(" -3- Zufällige Spieler-Auswahl\n");
enterString();
if (receiveBuffer[0] == '1') turn = 1;
if (receiveBuffer[0] == '2') turn = 2;
if (receiveBuffer[0] == '3') {
turn = rand() % 2 + 1;
writeString_P("Es beginnt der Spieler/Computer ");
writeInteger(turn, DEC);
writeString_P("!\n");
}
initFieldArray();
showBoard();
switch (gamemode) {
// Player 2 plays against player 1:
case PLAYER_PLAYER :
playerVplayer();
break;
// Computer 2 plays against player 1:
case COMPUTER_PLAYER :
computerVplayer();
break;
// Shows the computer demo:
case COMPUTER_COMPUTER :
computerDemo();
break;
}
}
/*****************************************************************************/
// Main function - The program starts here:
int main(void)
{
initRP6Control(); // Always call this first! The Processor will not work
// correctly otherwise.
initLCD(); // Initialize the LC-Display (LCD)
// Always call this before using the LCD!
// Write some text messages to the UART - just like on RP6Base:
writeString_P("\n\n _______________________\n");
writeString_P(" \\| RP6 ROBOT SYSTEM |/\n");
writeString_P(" \\_-_-_-_-_-_-_-_-_-_/\n\n");
writeString_P("Kalaha for RP6 CONTROL!\n");
// Set the four Status LEDs:
setLEDs(0b1111);
mSleep(500);
setLEDs(0b0000);
showScreenLCD("################", "################");
mSleep(1500);
showScreenLCD("<<RP6 Control>>", "<<LC - DISPLAY>>");
mSleep(2500);
showScreenLCD(" Kalaha 1.0 ", " ************ ");
mSleep(2500);
clearLCD(); // Clear the whole LCD Screen
srand(get_seed());
while(true) {
newGame();
}
return 0;
}
/******************************************************************************
* Additional info
* ****************************************************************************
* Changelog:
* - v. 1.0 (initial release) 24.05.2011 by Dirk
*
* ****************************************************************************
*/
/*****************************************************************************/
Viel Spaß!
Lesezeichen