I tried everything and my Game of Fifteen is one error away from being compiled.
The only issue is that somehow I get the "unused parameter error", telling me that my code does not make use of the tile parameter...which it should!
Here's the error:
fifteeen.c:312:15: error: unused parameter 'tile' [-Werror,-Wunused-parameter]
void swaptile(tile)
1 error generated.
And here's the code
/**
* fifteen.c
*
* Implements Game of Fifteen (generalized to d x d).
*
* Usage: fifteen d
*
* whereby the board's dimensions are to be d x d,
* where d must be in [DIM_MIN,DIM_MAX]
*
* Note that usleep is obsolete, but it offers more granularity than
* sleep and is simpler to use than nanosleep; `man usleep` for more.
*/
#define _XOPEN_SOURCE 500
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// constants
#define DIM_MIN 3
#define DIM_MAX 9
// board
int board[DIM_MAX][DIM_MAX];
// dimensions
int d;
int tile_row;
int tile_column;
int blank_row;
int blank_column;
// prototypes
void clear(void);
void greet(void);
void init(void);
void draw(void);
bool move(int tile);
bool won(void);
void search(int tile);
bool legalmove(void);
void swaptile(int tile);
int main(int argc, string argv[])
{
// ensure proper usage
if (argc != 2)
{
printf("Usage: fifteen d\n");
return 1;
}
// ensure valid dimensions
d = atoi(argv[1]);
if (d < DIM_MIN || d > DIM_MAX)
{
printf("Board must be between %i x %i and %i x %i, inclusive.\n",
DIM_MIN, DIM_MIN, DIM_MAX, DIM_MAX);
return 2;
}
// open log
FILE* file = fopen("log.txt", "w");
if (file == NULL)
{
return 3;
}
// greet user with instructions
greet();
// initialize the board
init();
// accept moves until game is won
while (true)
{
// clear the screen
clear();
// draw the current state of the board
draw();
// log the current state of the board (for testing)
for (int i = 0; i < d; i++)
{
for (int j = 0; j < d; j++)
{
fprintf(file, "%i", board[i][j]);
if (j < d - 1)
{
fprintf(file, "|");
}
}
fprintf(file, "\n");
}
fflush(file);
// check for win
if (won())
{
printf("ftw!\n");
break;
}
// prompt for move
printf("Tile to move: ");
int tile = GetInt();
// quit if user inputs 0 (for testing)
if (tile == 0)
{
break;
}
// log move (for testing)
fprintf(file, "%i\n", tile);
fflush(file);
// move if possible, else report illegality
if (!move(tile))
{
printf("\nIllegal move.\n");
usleep(500000);
}
// sleep thread for animation's sake
usleep(500000);
}
// close log
fclose(file);
// success
return 0;
}
/**
* Clears screen using ANSI escape sequences.
*/
void clear(void)
{
printf("\033[2J");
printf("\033[%d;%dH", 0, 0);
}
/**
* Greets player.
*/
void greet(void)
{
clear();
printf("WELCOME TO GAME OF FIFTEEN\n");
usleep(2000000);
}
/**
* Initializes the game's board with tiles numbered 1 through d*d - 1
* (i.e., fills 2D array with values but does not actually print them).
*/
void init(void)
{
/* total tiles to be initialised */
int total_tiles = (d * d) - 1;
/* loop 2D array with outer loop row: fill tiles one by one
left to right (column) then top to bottom (rows)*/
for (int index_row = 0; index_row < d ; index_row++)
{
for (int index_column = 0; index_column < d ; index_column++)
{
board [index_row][index_column] = total_tiles;
total_tiles--;
}
printf("\n");
}
/* if d even, the number of tiles odd, then swap tile 1 and 2 */
if (((d*d) - 1) % 2 != 0)
{
int temp = board[d-1][d-2];
board[d-1][d-2] = board[d-1][d-3];
board[d-1][d-3] = temp;
}
/* mark where your empty tile is */
blank_row = d-1;
blank_column = d-1;
}
/**
* Prints the board in its current state.
*/
void draw(void)
{
/* Print the board, if tile 0 print underscore */
for (int index_row = 0; index_row < d; index_row++)
{
for (int index_column = 0; index_column < d; index_column++)
{
if (board[index_row][index_column] < 10)
{
if (board[index_row][index_column] == 0)
{
printf(" [_] ");
}
else
{
printf(" [%d] ", board[index_row][index_column]);
}
}
else
{
printf("[%d] ", board[index_row][index_column]);
}
}
/* print space before printing next line */
printf("\n");
}
}
/**
* If tile borders empty space, moves tile and returns true, else
* returns false.
*/
bool move(int tile)
{
/* sanity check for tile's existance */
if (tile > (d*d)-1 || tile < 1)
return false;
/* linear search for tile inputted by user */
search(tile);
/* Once we found the tile, we swap if move is legal */
if (legalmove())
{
swaptile(tile);
return true;
}
else
return false;
return false;
}
/**
* Returns true if game is won (i.e., board is in winning configuration),
* else false.
*/
bool won(void)
{
/*set counter and check if last tile empty */
int counter = 1;
/* Check if every tile corresponds to the an arthmetic sequence start = 1, constant = +1 */
for(int index_row = 0; index_row < d; index_row++)
{
for(int index_column = 0; index_column < d; index_column++)
{
if (board[index_row][index_column] == counter)
counter++;
}
}
if (counter == d*d && board[d-1][d-1] == 0)
return true;
else
return false;
}
/* linear search for tile */
void search(int tile)
{
for (int index_row = 0; index_row < d; index_row++)
{
for(int index_column = 0; index_column < d; index_column++)
{
/* if tile found */
if (board[index_row][index_column] == tile)
{
tile_row = index_row;
tile_column = index_column;
}
}
}
}
/* check if blank space bordering with the tile found*/
bool legalmove(void)
{
/* check if space on top row */
if (tile_row > 0 && board[tile_row - 1][tile_column] == 0)
return true;
/* bottom */
if (tile_row < d - 1 && board[tile_row + 1][tile_column] == 0)
return true;
/* left */
if (tile_column > 0 && board[tile_row][tile_column - 1] == 0)
return true;
/* right */
if (tile_column < d - 1 && board[tile_row][tile_column + 1] == 0)
return true;
else
return false;
}
/* swap and update tile position */
void swaptile(tile)
{
int temp = board[tile_row][tile_column];
board[tile_row][tile_column] = board[blank_row][blank_column];
board[blank_row][blank_column] = temp;
blank_row = tile_row;
blank_column = tile_column;
}
You get the warning because in swaptile the tile parameter is actually not used (check yourself) and because you compiled with -Wunused-parameter:
/* swap and update tile position */
void swaptile(tile)
{
/* tile parameter is not used in this function */
int temp = board[tile_row][tile_column];
board[tile_row][tile_column] = board[blank_row][blank_column];
board[blank_row][blank_column] = temp;
blank_row = tile_row;
blank_column = tile_column;
}
... and it is actually an error because you compiled with -Werror, which considers all warnings as errors.
Sou you just should have
void swaptile()
instead of
void swaptile(tile)
BTW: void swaptile(tile) should be void swaptile(int tile), (implicit int types in function declarations are somewhat depcrecated and some compiler don't even accept it anymore).
Related
Im trying to write a program that wont compile. The error i keep receiving reads like this
expected expression
destroyFallingStone (int map[][SIZE], int column);
It happened after i added the destroyFallingStone function and Ive checked the function and function prototype for any syntax errors. I don't know where Ive made a mistake.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define SIZE 15
#define EMPTY 0
#define STONE 1
// TODO: Add any extra #defines here.
// TODO: Add any extra function prototypes here.
void printMap(int map[SIZE][SIZE], int playerX);
void destroyFallingStone (int map[][SIZE], int column);
int main (void) {
// This line creates our 2D array called "map" and sets all
// of the blocks in the map to EMPTY.
int map[SIZE][SIZE] = {EMPTY};
// This line creates out playerX variable. The player starts in the
// middle of the map, at position 7.
int playerX = SIZE / 2;
printf("How many lines of stone? ");
// TODO: Scan in the number of lines of blocks.
int linesOfStone;
scanf("%d", &linesOfStone);
printf("Enter lines of stone:\n");
// TODO: Scan in the lines of blocks.
int rowPos;
int columnPos;
int stoneLength;
int stoneValue;
int i = 0;
while (i < linesOfStone) {
scanf("%d %d %d %d", &rowPos, &columnPos, &stoneLength, &stoneValue);
if ( 0 <= rowPos && rowPos < SIZE &&
0 <= columnPos && columnPos < SIZE
&& columnPos + stoneLength - 1 < SIZE) {
int j = 0;
while (j < stoneLength) {
map[rowPos][columnPos + j] = STONE;
j++;
}
}
i++;
}
printMap(map, playerX);
// TODO: Scan in commands until EOF.
// After each command is processed, you should call printMap.
int quitLoop = 0;
int playerDirection = 0;
int playerMovement = 0;
while (quitLoop != 1) {
scanf("%d %d", &playerMovement, &playerDirection);
if ( playerMovement == 1 &&
playerDirection == 1 && playerX < (SIZE - 1)) {
//check player is within bounds
playerX++;
} else if ( playerMovement == 1 &&
playerDirection == -1 && playerX > 0 ) {
playerX--;
} else if ( playerMovement == 2) { // call function for destroying stones
destroyFallingStone (int map[][SIZE], int column);
}
printMap(map, playerX);
}
return 0;
}
// Print out the contents of the map array. Then print out the player line
// which will depends on the playerX variable.
void printMap(int map[SIZE][SIZE], int playerX) {
// Print values from the map array.
int i = 0;
while (i < SIZE) {
int j = 0;
while (j < SIZE) {
printf("%d ", map[i][j]);
j++;
}
printf("\n");
i++;
}
// Print the player line.
i = 0;
while (i < playerX) {
printf(" ");
i++;
}
printf("P\n");
}
//destroys the closes 2 stones
void destroyFallingStone (int map[][SIZE], int column) {
int i = 0;
int j = 0;
while (j < 3) {
while (i < 15 && map[i][column] != STONE) { //finding the first stone
i++;
}
// if there is a stone, destroy it
if (map[i][column] == STONE) {
map[i][column] = EMPTY;
}
i++;
}
}
You're not calling the function correctly:
} else if ( playerMovement == 2) { // call function for destroying stones
destroyFallingStone (int map[][SIZE], int column);
}
What you have here looks more like a declaration than a function call. You instead want:
} else if ( playerMovement == 2) { // call function for destroying stones
destroyFallingStone(map,column);
}
The problem is in line: 75 destroyFallingStone (int map[][SIZE], int column);. This should be a call to function destroyFallingStone. Something like destroyFallingStone (map, columnPos);.
Following is corrected code. See it compiling here:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define SIZE 15
#define EMPTY 0
#define STONE 1
// TODO: Add any extra #defines here.
// TODO: Add any extra function prototypes here.
void printMap(int map[SIZE][SIZE], int playerX);
void destroyFallingStone (int map[][SIZE], int column);
int main (void) {
// This line creates our 2D array called "map" and sets all
// of the blocks in the map to EMPTY.
int map[SIZE][SIZE] = {EMPTY};
// This line creates out playerX variable. The player starts in the
// middle of the map, at position 7.
int playerX = SIZE / 2;
printf("How many lines of stone? ");
// TODO: Scan in the number of lines of blocks.
int linesOfStone;
scanf("%d", &linesOfStone);
printf("Enter lines of stone:\n");
// TODO: Scan in the lines of blocks.
int rowPos;
int columnPos;
int stoneLength;
int stoneValue;
int i = 0;
while (i < linesOfStone) {
scanf("%d %d %d %d", &rowPos, &columnPos, &stoneLength, &stoneValue);
if ( 0 <= rowPos && rowPos < SIZE &&
0 <= columnPos && columnPos < SIZE
&& columnPos + stoneLength - 1 < SIZE) {
int j = 0;
while (j < stoneLength) {
map[rowPos][columnPos + j] = STONE;
j++;
}
}
i++;
}
printMap(map, playerX);
// TODO: Scan in commands until EOF.
// After each command is processed, you should call printMap.
int quitLoop = 0;
int playerDirection = 0;
int playerMovement = 0;
while (quitLoop != 1) {
scanf("%d %d", &playerMovement, &playerDirection);
if ( playerMovement == 1 &&
playerDirection == 1 && playerX < (SIZE - 1)) {
//check player is within bounds
playerX++;
} else if ( playerMovement == 1 &&
playerDirection == -1 && playerX > 0 ) {
playerX--;
} else if ( playerMovement == 2) { // call function for destroying stones
destroyFallingStone (map, columnPos);
}
printMap(map, playerX);
}
return 0;
}
// Print out the contents of the map array. Then print out the player line
// which will depends on the playerX variable.
void printMap(int map[SIZE][SIZE], int playerX) {
// Print values from the map array.
int i = 0;
while (i < SIZE) {
int j = 0;
while (j < SIZE) {
printf("%d ", map[i][j]);
j++;
}
printf("\n");
i++;
}
// Print the player line.
i = 0;
while (i < playerX) {
printf(" ");
i++;
}
printf("P\n");
}
//destroys the closes 2 stones
void destroyFallingStone (int map[][SIZE], int column) {
int i = 0;
int j = 0;
while (j < 3) {
while (i < 15 && map[i][column] != STONE) { //finding the first stone
i++;
}
// if there is a stone, destroy it
if (map[i][column] == STONE) {
map[i][column] = EMPTY;
}
i++;
}
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I'm taking cs50 and on pset3 fifteen. The game looks fine except when inputing a number to replace '_' with. At the start when the game asks for a number/tile to move, I input the number for '_' to move to but it doesn't replace '_' with the chosen number. This only happens when dimensions are even and choosing the numbers to the left of '_' at the start of the game. Thanks!
#define _XOPEN_SOURCE 500
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// constants
#define DIM_MIN 3
#define DIM_MAX 9
// board
int board[DIM_MAX][DIM_MAX];
// dimensions
int d;
// prototypes
void clear(void);
void greet(void);
void init(void);
void draw(void);
bool move(int tile);
bool won(void);
int main(int argc, string argv[])
{
// ensure proper usage
if (argc != 2)
{
printf("Usage: fifteen d\n");
return 1;
}
// ensure valid dimensions
d = atoi(argv[1]);
if (d < DIM_MIN || d > DIM_MAX)
{
printf("Board must be between %i x %i and %i x %i, inclusive.\n",
DIM_MIN, DIM_MIN, DIM_MAX, DIM_MAX);
return 2;
}
// open log
FILE *file = fopen("log.txt", "w");
if (file == NULL)
{
return 3;
}
// greet user with instructions
greet();
// initialize the board
init();
// accept moves until game is won
while (true)
{
// clear the screen
clear();
// draw the current state of the board
draw();
// log the current state of the board (for testing)
for (int i = 0; i < d; i++)
{
for (int j = 0; j < d; j++)
{
fprintf(file, "%i", board[i][j]);
if (j < d - 1)
{
fprintf(file, "|");
}
}
fprintf(file, "\n");
}
fflush(file);
// check for win
if (won())
{
printf("ftw!\n");
break;
}
// prompt for move
printf("Tile to move: ");
int tile = get_int();
// quit if user inputs 0 (for testing)
if (tile == 0)
{
break;
}
// log move (for testing)
fprintf(file, "%i\n", tile);
fflush(file);
// move if possible, else report illegality
if (!move(tile))
{
printf("\nIllegal move.\n");
usleep(500000);
}
// sleep thread for animation's sake
usleep(500000);
}
// close log
fclose(file);
// success
return 0;
}
/**
* Clears screen using ANSI escape sequences.
*/
void clear(void)
{
printf("\033[2J");
printf("\033[%d;%dH", 0, 0);
}
/**
* Greets player.
*/
void greet(void)
{
clear();
printf("WELCOME TO GAME OF FIFTEEN\n");
usleep(2000000);
}
/**
* Initializes the game's board with tiles numbered 1 through d*d - 1
* (i.e., fills 2D array with values but does not actually print them).
*/
void init(void)
{
int dimensionNum = (d * d) - 1;
for (int i = 0; i < d; i++)
{
for (int j = 0; j < d; j++)
{
board[i][j] = dimensionNum--;
}
}
if (d % 2 == 0)
{
board[d - 1][d - 2] = 2;
board[d - 1][d - 3] = 1;
}
}
/**
* Prints the board in its current state.
*/
void draw(void)
{
for (int i = 0; i < d; i++)
{
for (int j = 0; j < d; j++)
{
if (board[i][j] == 0)
{
printf(" _");
}
else
{
printf("%3i", board[i][j]);
}
}
printf("\n");
}
}
/**
* If tile borders empty space, moves tile and returns true, else
* returns false.
*/
bool move(int tile)
{
int _i, _j;
for (int i = 0; i < d; i++)
{
for (int j = 0; j < d; j++)
{
if (board[i][j] == tile)
{
_i = i;
_j = j;
}
}
}
if (board[_i + 1][_j] == 0)
{
board[_i + 1][_j] = board[_i][_j];
board[_i][_j] = 0;
return true;
}
else if (board[_i - 1][_j] == 0)
{
board[_i - 1][_j] = board[_i][_j];
board[_i][_j] = 0;
return true;
}
else if (board[_i][_j - 1] == 0)
{
board[_i][_j - 1] = board[_i][_j];
board[_i][_j] = 0;
return true;
}
else if (board[_i][_j + 1] == 0)
{
board[_i][_j + 1] = board[_i][_j];
board[_i][_j] = 0;
return true;
}
return false;
}
After you try to find the tile in the board in the move function, you should check if the obtained array indices are valid or not. Because, in case user inputs number outside the permitted values, then some garbage value is used for further condition checking. So make the _i and _j variables -1 or something at the beginning and check if they are - 1 after the for loop in the move function. If they are, it means you have illegal input and can directly return false. Otherwise, you can proceed with the condition checking. Other code seems to be fine. You could also try using the debugger or go through the walkthrough for better understanding.
If this answers your question, click the green checkmark to close this question.
My functions (init,draw,move and win) are not working properly. Can anyone shed some light on where I am going wrong?
init - should initialer the board and swap 1 and 2.
draw - should draw the current state of the board.
move - should move the tile if not illegal move.
win - should check the board if its in descending order.
/**
* fifteen.c
*
* Computer Science 50
* Problem Set 3
*
* Implements Game of Fifteen (generalized to d x d).
*
* Usage: fifteen d
*
* whereby the board's dimensions are to be d x d,
* where d must be in [DIM_MIN,DIM_MAX]
*
* Note that usleep is obsolete, but it offers more granularity than
* sleep and is simpler to use than nanosleep; `man usleep` for more.
*/
#
define _XOPEN_SOURCE 500
# include < cs50.h > #include < stdio.h > #include < string.h > #include <
stdlib.h > #include < unistd.h >
// constants
#define DIM_MIN 3# define DIM_MAX 9
// board
int board[DIM_MAX][DIM_MAX];
// dimensions
int d;
// prototypes
void clear(void);
void greet(void);
void init(void);
void draw(void);
bool move(int tile);
bool won(void);
void metrytoexchange(int * a, int * b);
int main(int argc, string argv[]) {
// ensure proper usage
if (argc != 2) {
printf("Usage: fifteen d\n");
return 1;
}
// ensure valid dimensions
d = atoi(argv[1]);
if (d < DIM_MIN || d > DIM_MAX) {
printf("Board must be between %i x %i and %i x %i, inclusive.\n",
DIM_MIN, DIM_MIN, DIM_MAX, DIM_MAX);
return 2;
}
// open log
FILE * file = fopen("log.txt", "w");
if (file == NULL) {
return 3;
}
// greet user with instructions
greet();
// initialize the board
init();
// accept moves until game is won
while (true) {
// clear the screen
clear();
// draw the current state of the board
draw();
// log the current state of the board (for testing)
for (int i = 0; i < d; i++) {
for (int j = 0; j < d; j++) {
fprintf(file, "%i", board[i][j]);
if (j < d - 1) {
fprintf(file, "|");
}
}
fprintf(file, "\n");
}
fflush(file);
// check for win
if (won()) {
printf("ftw!\n");
break;
}
// prompt for move
printf("Tile to move: ");
int tile = GetInt();
// quit if user inputs 0 (for testing)
if (tile == 0) {
break;
}
// log move (for testing)
fprintf(file, "%i\n", tile);
fflush(file);
// move if possible, else report illegality
if (!move(tile)) {
printf("\nIllegal move.\n");
usleep(500000);
}
// sleep thread for animation's sake
usleep(500000);
}
// close log
fclose(file);
// success
return 0;
}
/**
* Clears screen using ANSI escape sequences.
*/
void clear(void) {
printf("\033[2J");
printf("\033[%d;%dH", 0, 0);
}
/**
* Greets player.
*/
void greet(void) {
clear();
printf("WELCOME TO GAME OF FIFTEEN\n");
usleep(2000000);
}
/**
* Initializes the game's board with tiles numbered 1 through d*d - 1
* (i.e., fills 2D array with values but does not actually print them).
*/
void init(void) {
// TODO
int i, j;
int k = d * d - 1;
for (i = 0; i < d; i++) {
for (j = 0; j < d; j++) {
board[i][j] = k--;
if (k % 2 != 0) {
break;
} else {
if ((board[i][j] == 2) && (board[i][j - 1] == 1))
metrytoexchange( & board[i][j], & board[i][j - 1]);
}
}
}
}
/**
* Prints the board in its current state.
*/
void draw(void) { // TODO
int k = d * d - 1;
for (int i = 0; i < d; i++) {
for (int j = 0; j < d; j++) {
board[i][j] = k--;
if (board[i][j] == 0) {
printf("_");
} else
printf("%d \t", board[i][j]);
}
printf("\n");
}
}
void metrytoexchange(int * a, int * b) {
int temp = * a; *
a = * b; *
b = temp;
}
/**
* If tile borders empty space, moves tile and returns true, else
* returns false.
*/
bool move(int tile) {
int k = d * d - 1;
int blank_space = 0;
//dont go beyond the grid
for (int i = 0; i < d; i++) {
for (int j = 0; j < d; j++) {
if (tile < k && tile > 0 && tile == board[i][j]) {
continue;
} else {
break;
}
//iterate to check the position of of blank tile;left to right up and down if not return false
if (board[i - 1][j] != blank_space || board[i][j - 1] !=
blank_space || board[i + 1][j] != blank_space || board[i][j +
1
] != blank_space) {
return false;
}
//else swap tile with blank_space
else {
metrytoexchange( & tile, & blank_space);
return true;
}
}
}
return false;
}
/**
* Returns true if game is won (i.e., board is in winning configuration),
* else false.
*/
bool won(void) {
// TODO
// creat a variable that increases as the loop condition increases.let it start from 1 increasing
int win_board[d][d];
for (int i = 0; i < d; i++) {
for (int j = 0; j < d; j++) {
if (win_board[i][j] == board[i][j]) {
return true;
} else {
return false;
}
}
}
return false;
}
This question already has answers here:
Are negative array indexes allowed in C?
(9 answers)
Closed 6 years ago.
/**
* fifteen.c
*
* Computer Science 50
* Problem Set 3
*
* Implements Game of Fifteen (generalized to d x d).
*
* Usage: fifteen d
*
* whereby the board's dimensions are to be d x d,
* where d must be in [DIM_MIN,DIM_MAX]
*
* Note that usleep is obsolete, but it offers more granularity than
* sleep and is simpler to use than nanosleep; `man usleep` for more.
*/
#define _XOPEN_SOURCE 500
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// constants
#define DIM_MIN 3
#define DIM_MAX 9
// board
int board[DIM_MAX][DIM_MAX];
int b;
// dimensions
int d;
// prototypes
void clear(void);
void greet(void);
void init(void);
void draw(void);
bool move(int tile);
bool won(void);
int main(int argc, string argv[])
{
// ensure proper usage
if (argc != 2)
{
printf("Usage: fifteen d\n");
return 1;
}
// ensure valid dimensions
d = atoi(argv[1]);
if (d < DIM_MIN || d > DIM_MAX)
{
printf("Board must be between %i x %i and %i x %i, inclusive.\n",
DIM_MIN, DIM_MIN, DIM_MAX, DIM_MAX);
return 2;
}
// open log
FILE* file = fopen("log.txt", "w");
if (file == NULL)
{
return 3;
}
// greet user with instructions
greet();
// initialize the board
init();
// accept moves until game is won
while (true)
{
// clear the screen
clear();
// draw the current state of the board
draw();
// log the current state of the board (for testing)
for (int i = 0; i < d; i++)
{
for (int j = 0; j < d; j++)
{
fprintf(file, "%i", board[i][j]);
if (j < d - 1)
{
fprintf(file, "|");
}
}
fprintf(file, "\n");
}
fflush(file);
// check for win
if (won())
{
printf("ftw!\n");
break;
}
// prompt for move
printf("Tile to move: ");
int tile = GetInt();
// quit if user inputs 0 (for testing)
if (tile == 0)
{
break;
}
// log move (for testing)
fprintf(file, "%i\n", tile);
fflush(file);
// move if possible, else report illegality
if (!move(tile))
{
printf("\nIllegal move.\n");
usleep(500000);
}
// sleep thread for animation's sake
usleep(500000);
}
// close log
fclose(file);
// success
return 0;
}
/**
* Clears screen using ANSI escape sequences.
*/
void clear(void)
{
printf("\033[2J");
printf("\033[%d;%dH", 0, 0);
}
/**
* Greets player.
*/
void greet(void)
{
clear();
printf("WELCOME TO GAME OF FIFTEEN\n");
usleep(2000000);
}
/**
* Initializes the game's board with tiles numbered 1 through d*d - 1
* (i.e., fills 2D array with values but does not actually print them).
*/
void init(void)
{
for(int i = 0, j = 0, k = ((d*d)-1); i < d; j++, k--)
{
if(j == d)
{
i = i + 1;
j = 0;
}
board[i][j] = k;
}
if((d*d)%2 == 0)
{
board[(d-1)][(d-2)] = 2;
board[(d-1)][(d-3)] = 1;
}
board[(d-1)][(d-1)] = 0;
b = board[(d-1)][(d-1)];
}
/**
* Prints the board in its current state.
*/
void draw(void)
{
for(int i = 0, j = 0; i !=(d-1) || j!=d; j++)
{
if(j == d)
{
i = i + 1;
j = 0;
printf("\n");
}
if(board[i][j] == 0) //b used to be 99
{
printf(" _");
}
else
{
printf(" %2d", board[i][j]);
}
}
printf("\n");
}
/**
* If tile borders empty space, moves tile and returns true, else
* returns false.
*/
bool move(int tile)
{
//find tile
for(int i = 0, j = 0; i !=(d-1) || j!=d; j++)
{
if(j == d)
{
i = i + 1;
j = 0;
}
if (board[i][j] == tile)
{
//check if tile position is in valid perimeter of blank space
if (board[i+1][j] == b)
{
board[i+1][j] = tile;
board[i][j] = 0;
b = board[i][j];
return true;
}
if (board[i-1][j] == b)
{
board[i-1][j] = tile;
board[i][j] = 0;
b = board[i][j];
return true;
}
if (board[i][j+1] == b)
{
board[i][j+1] = tile;
board[i][j] = 0;
b = board[i][j];
return true;
}
if (board[i][j-1] == b)
{
printf("%i", board[i][j-1]);
board[i][j-1] = tile;
board[i][j] = 0;
b = board[i][j];
return true;
}
}
}
return false;
}
/**
* Returns true if game is won (i.e., board is in winning configuration),
* else false.
*/
bool won(void)
{
for(int i = 0, j = 0, k = 1; i !=(d-1) || j!=d; j++)
{
if(j == d)
{
i = i + 1;
j = 0;
}
if (k == (d*d)-1)
{
return true;
}
if (board[i][j] == k)
{
k = k + 1;
}
}
return false;
}
I originally had
board[(d-1)][(d-1)] = 0;
equal to 99 along with the move function look for 99. For my problem set I was supposed to use 0. Once I changed 99 to 0, for some reason 0 is being found if board[i][j-1] even if that means board[2][-1]. Why is that allow/why does that equal 0? and how can I disable this?
You have int board[DIM_MAX][DIM_MAX]; where #define DIM_MIN 3 and the memory allocated for elements are contiguous, so typically you will access board[1][2] by using board[2][-1]. But this is undefined behavior, which allows anything to happen, and you mustn't use that.
Quote from N1570 J.2 Undefined behavior:
An array subscript is out of range, even if an object is apparently accessible with the
given subscript (as in the lvalue expression a[1][7] given the declaration int
a[4][5]) (6.5.6).
board[2][-1]. Why is that allow
C does allow you access out of range of an array. But it is Undefined Behavior.
why does that equal 0?
By accident. It's Undefined Behavior and it could be anything.
I am new to game tree algorithms, and i've tried to implement a simple tictactoe game which utilizes the NegaMax algorithm to evaluate tile scores for the computer AI player.
However, the AI behaves not in a smart way (i can win all the time, because the AI does not block my moves), and i think i implemented the NegaMax algorithm wrongly.
If anyone could help me with the following code, it would be great. I spent such a long time trying different things out, but i never got it to work.
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <limits.h>
#include "game_board.h"
/**
* Constructs a new game board.
*
* #param size the size of the board (the length of fields)
* #return a new GameBoard
*/
GameBoard* game_board_new(int size)
{
GameBoard* board = (GameBoard*) calloc(1, sizeof(GameBoard));
board->size = size;
board->turn = WHITE;
board->fields = (enum Player*) calloc(size, sizeof(enum Player));
return board;
}
/**
* Releases a game board.
*
* #param board the GameBoard to release
*/
void game_board_free(GameBoard* board)
{
free(board->fields);
free(board);
}
/**
* Copies a game board.
*
* #param original the GameBoard to copy
* #return a new GameBoard which is copied from the original
*/
GameBoard* game_board_copy(GameBoard* original)
{
GameBoard* copy;
int i;
copy = game_board_new(original->size);
copy->size = original->size;
copy->turn = original->turn;
for (i = 0; i < original->size; i++)
{
copy->fields[i] = original->fields[i];
}
return copy;
}
/**
* Returns the winner for the current game or EMPTY if
* there is no winner, yet.
*
* #param board the GameBoard to check
* #return the winning Player or EMPTY
*/
enum Player game_board_check_winner(GameBoard* board)
{
enum Player* f;
f = board->fields;
// Columns
if (f[0] != EMPTY && f[0] == f[3] && f[0] == f[6])
return f[0];
if (f[1] != EMPTY && f[1] == f[4] && f[1] == f[7])
return f[1];
if (f[2] != EMPTY && f[2] == f[5] && f[2] == f[8])
return f[2];
// Rows
if (f[0] != EMPTY && f[0] == f[1] && f[1] == f[2])
return f[0];
if (f[3] != EMPTY && f[3] == f[4] && f[4] == f[5])
return f[3];
if (f[6] != EMPTY && f[6] == f[7] && f[7] == f[8])
return f[6];
// Diagonal
if (f[0] != EMPTY && f[0] == f[4] && f[4] == f[8])
return f[0];
if (f[2] != EMPTY && f[2] == f[4] && f[4] == f[6])
return f[2];
return EMPTY;
}
void game_board_toggle_player(GameBoard* board)
{
if (board->turn == WHITE)
board->turn = BLACK;
else if (board->turn == BLACK)
board->turn = WHITE;
}
int game_board_is_ended(GameBoard* board)
{
return game_board_check_winner(board) || game_board_is_full(board);
}
static int evaluate(GameBoard* board)
{
enum Player winner = game_board_check_winner(board);
if (winner != EMPTY && winner == board->turn)
{
return 1;
}
else if (winner != EMPTY && winner != board->turn)
{
return -1;
}
else
{
return 0;
}
}
int negamax(GameBoard* board, int depth)
{
int bestvalue = INT_MIN;
int i;
int value;
enum Player winner = game_board_check_winner(board);
if (winner != EMPTY || depth == 0)
return evaluate(board);
for (i = 0; i < board->size; i++)
{
if (board->fields[i] == EMPTY)
{
GameBoard* copy = game_board_copy(board);
game_board_make_move(copy, i);
game_board_toggle_player(board);
value = -negamax(copy, depth-1);
bestvalue = max(value, bestvalue);
game_board_free(copy);
}
}
return bestvalue;
}
/**
* Evaluates the current board according to NegaMax
* algorithm.
*
* #param board the GameBoard to evaluate
* #return a NegaMax score
*/
int game_board_evaluate(GameBoard* board, int* best_pos)
{
int i;
int maxVal;
int val;
maxVal = INT_MIN;
for (i = 0; i < board->size; i++)
{
if (board->fields[i] == EMPTY)
{
val = negamax(board, 9);
if (val > maxVal)
{
maxVal = val;
printf("Best move: %i\n", i);
(*best_pos) = i;
}
}
}
return maxVal;
}
int game_board_is_full(GameBoard* board)
{
int i;
for (i = 0; i < board->size; i++)
{
if (board->fields[i] == 0)
{
return 0;
}
}
return 1;
}
void game_board_make_move(GameBoard* board, int pos)
{
board->fields[pos] = board->turn;
}
/**
* Prints a game board.
*
* #param board the GameBoard to print
*/
void game_board_print(GameBoard* board)
{
int i;
for (i = 0; i < board->size; i++)
{
printf("%i (%i)\t", board->fields[i], i);
if (i == 2 || i == 5)
printf("\n");
}
printf("\n");
}
In a NegaMax framework the evaluation function has to look different. Try following evaluation function:
int evaluateNegaMax(GameBoard* board)
{
if (board->turn == MAXPLAYER)
return evaluate(board);
else
return -evaluate(board);
}
The chessprogramming site explains why:
In order for negaMax to work, your Static Evaluation function must return a score relative to the side to being evaluated
Shouldn't
game_board_toggle_player(board);
be
game_board_toggle_player(copy);
?
The usual error is to start with MIN at each level rather than passing values down the tree. I haven't studied your code in detail to compare with the paradigmatic negamax algorithm, but it seems that you may be making that error.