NegaMax Algorithm and TicTacToe...What's wrong? - c

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.

Related

process exits prematurely C programming

there is a part where the program asks the user to enter Y or N and then loops beck when I choose N or else it will end the while loop and continue. when I choose the Y for the first time the program works fine but when I choose N and then Y after my program exits even if it does not encounter the return keyword from the main
and it exits with a garbage return value. It stops at system("cls");. Can anyone tell me what's wrong with this code. Note:
Statistician is an integer pointer type that I created with typedef. And, I've also declared the SIZE variable in the survey.h file
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include "survey.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char *argv[]) {
SIZE = 10;
int c, count = 0, item = 0;
Statistician arr;
float mea, med;
arr = (int*)calloc(10, sizeof(int));
printf("Enter 10 answers\n");
while(count < SIZE) // this is the while loop that loops until Y is chosen by the user in the add function
{
while(item > 9 || item < 1)
{
scanf("%d", &item);
}
++count;
add(arr, &count, &SIZE, item);
item = 0;
}
system("cls");
mea = mean(arr, count);
med = median(arr, count);
printf("mean = %f\n", mea);
printf("median = %f\n", med);
return 0;
}
definition of add() function:
void add(Statistician answer, int *count, int *SIZE, int item)
{
int i, j, temp;
bool swapped;
char choice;
answer[*count - 1] = item;
for(i = 0; i < *count - 1; i++)
{
swapped = false;
for(j = 0; j < *count - i - 1; j++)
{
if(answer[j] > answer[j + 1])
{
temp = answer[j];
answer[j] = answer[j + 1];
answer[j + 1] = temp;
swapped = true;
}
}
if(swapped == false)
break;
}
if(*count == *SIZE)
{
printf("Array is full do you want to compute now?\n");
while(toupper(choice) != 'N' && toupper(choice) != 'Y') // The part where the program ask for Y or N.
{
choice = toupper(getch());
}
if(toupper(choice) == 'Y') // returns without changing the value of SIZE thus ending the while loop at main
{
return;
}
else if(toupper(choice) == 'N') // adds 10 to SIZE thus continuing the while loop in main and returns
{
printf("add another 10 answers\n");
*SIZE += 10;
realloc(answer, *SIZE);
}
}
return;
}
There are probably other issues (I'm not going to look too closely), but you certainly need to fix:
while(item > 9 || item < 1)
{
scanf("%d", &item);
}
If scanf matches zero items, then that is an infinite loop, with scanf repeatedly returning 0, reading the same data and not changing item. You must always check the value returned by scanf.
This is a serious bug:
realloc(answer, *SIZE);
You don't save the return value so you have lost the allocated memory. Further, you forgot the object size.
In principle you should do
Statistician tmp = realloc(answer, *SIZE * sizeof(int));
if (tmp == NULL)
{
// Error handling
// or just
exit(1);
}
answer = tmp;
However, that doesn't fully help. The problem is that it will only change the value of answer inside the function but not the value of arr in main. In order to change the value of arr you'll have to pass the address of arr to the function. Similar to what you have done with SIZE. BTW: Why do you pass counter as a pointer? You never change it in the function, so it unnecessary to pass a pointer.
Also your current code doesn't initialize choice.
Change
printf("Array is full do you want to compute now?\n");
while(toupper(choice) != 'N' && toupper(choice) != 'Y') // The part where the program ask for Y or N.
to
printf("Array is full do you want to compute now?\n");
choice = ' ';
while(toupper(choice) != 'N' && toupper(choice) != 'Y') // The part where the program ask for Y or N.
or better:
printf("Array is full do you want to compute now?\n");
do
{
choice = toupper(getch());
} while(toupper(choice) != 'N' && toupper(choice) != 'Y');
BTW:
Since you have choice = toupper(getch()); you don't need toupper(choice) != 'N'. Simply do choice != 'N'
All that said - why do you want to ask the question inside the function? Your code will be much simpler if you do it in main.
Something like:
int main(void) {
int SIZE = 10;
int c, count = 0, item = 0;
int* arr;
float mea, med;
arr = calloc(10, sizeof(int));
printf("Enter 10 answers\n");
while(count < SIZE)
{
while(item > 9 || item < 1)
{
if (scanf("%d", &item) != 1) exit(1);
}
++count;
add(arr, count, item);
item = 0;
if (count == SIZE)
{
printf("Array is full do you want to compute now?\n");
char choice;
do
{
choice = toupper(getch());
} while(choice != 'N' && choice != 'Y');
if(choice == 'N')
{
printf("add another 10 answers\n");
SIZE += 10;
int* tmp = realloc(arr, SIZE * sizeof *arr);
if (tmp == NULL) exit(1); // or error handling
arr = tmp;
}
}
}
system("cls");
mea = mean(arr, count);
med = median(arr, count);
printf("mean = %f\n", mea);
printf("median = %f\n", med);
return 0;
}
void add(int* answer, int count, int item)
{
int i, j, temp;
bool swapped;
answer[count - 1] = item;
for(i = 0; i < count - 1; i++)
{
swapped = false;
for(j = 0; j < count - i - 1; j++)
{
if(answer[j] > answer[j + 1])
{
temp = answer[j];
answer[j] = answer[j + 1];
answer[j + 1] = temp;
swapped = true;
}
}
if(swapped == false)
break;
}
return;
}

Why is my program of game of fifteen printing '_' instead of the replaced number? [closed]

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.

error: unused parameter 'tile'

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).

Trouble with pset3 game of fifteen

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;
}

Why is array[i][-1] == 0? How does one prevent undefined variables from being used? [duplicate]

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.

Resources