Tic Tac Toe Minimax not working in certain placement - c

It would give me a board that looks like
==============
| X | 1 | O |
-------------
| 3 | X | 5 |
-------------
| O | 7 | 8 |
==============
The issue i think is that the first move is not in the middle which will cause the AI to lose.
#include <stdio.h>
#include <stdbool.h>
#define X 'X'
#define O 'O'
#define T 'T'
struct Move
{
int row;
int column;
};
void generateBoard(char board[3][3]);
bool canMakeMove(char board[3][3], struct Move move);
bool makeMove(char board[3][3], struct Move move, char moveSymbol);
void copyBoard(char originalBoard[3][3], char duplicateBoard[3][3]);
int minimax(char board[3][3], int depth, bool isMaximizing);
char generatePlayerChar(int player);
char checkWinner(char board[3][3]);
bool hasAvailableSpot(char board[3][3]);
void generateBoard(char board[3][3])
{
printf("\n"
"==============\n"
"| %c | %c | %c |\n"
"-------------\n"
"| %c | %c | %c |\n"
"-------------\n"
"| %c | %c | %c |\n"
"==============\n",
board[0][0], board[0][1], board[0][2],
board[1][0], board[1][1], board[1][2],
board[2][0], board[2][1], board[2][2]);
}
char generatePlayerChar(int player)
{
return ((player == 1) ? 'X' : 'O');
}
/// #brief Given a board, check if there's any available spot available to use
/// #param board
/// #return
bool hasAvailableSpot(char board[3][3])
{
int count = 0;
for (int rows = 0; rows < 3; rows++)
{
for (int cols = 0; cols < 3; cols++)
{
struct Move attemptedMove = {rows, cols};
if (canMakeMove(board, attemptedMove))
{
count++;
}
}
}
if (count > 0)
{
return true;
}
return false;
}
/// #brief Check for both vertical/horizontal/diagonal winner
/// #param board
/// #return Return 'X'/'O'/'T'/-1 depending on who won the game/tie
char checkWinner(char board[3][3])
{
for (int i = 0; i < 3; i++)
{
// Check horizontal winning condition
if ((board[i][0] == board[i][1]) && (board[i][1] == board[i][2]))
{
return board[i][0];
}
// Check vertical winning condition
if ((board[0][i] == board[1][i]) && (board[1][i] == board[2][i]))
{
return board[0][i];
}
}
// Check for diagonal winning condition
if ((board[0][0] == board[1][1]) && (board[1][1] == board[2][2]))
{
return board[0][0];
}
else if ((board[0][2] == board[1][1]) && (board[1][1] == board[2][0]))
{
return board[0][2];
}
// There's no winner so we check if there's any empty space left yet.
if (!hasAvailableSpot(board))
{
return 'T';
}
return -1;
}
/// #brief Place the character on the board
/// #param board
/// #param move
/// #param moveSymbol
/// #return Return true depending on whether it successfully modified the board
bool makeMove(char board[3][3], struct Move move, char moveSymbol)
{
if (canMakeMove(board, move))
{
board[move.row][move.column] = moveSymbol;
return true;
}
return false;
}
/// #brief Takes in a board and does out of bound checking as well as check on available spots on the board
/// #param board The board to check
/// #param move A single structure consisting of row,column
/// #return A boolean true/false whether you can place a move on a specific area on the board
bool canMakeMove(char board[3][3], struct Move move)
{
if ((move.row > 3) || (move.column > 3))
{
return false;
}
if ((board[move.row][move.column] != X) && (board[move.row][move.column] != O))
{
return true;
}
return false;
}
/// #brief Calls each available spot on the board and determine using minimax which is the next bestMove supposed to be
/// #param board Given a board, what's the best next move to do next
/// #return A structure which contains the best determined move to do next
struct Move getBestMove(char board[3][3])
{
int bestScore = -1000;
struct Move bestMove;
for (int rows = 0; rows < 3; rows++)
{
for (int cols = 0; cols < 3; cols++)
{
struct Move attemptedMove = {rows, cols};
if (canMakeMove(board, attemptedMove))
{
char duplicatedBoard[3][3];
copyBoard(board, duplicatedBoard);
// Attempt to put in the next possible move.
makeMove(duplicatedBoard, attemptedMove, X);
int tempScore = minimax(duplicatedBoard, 0, false);
if (tempScore > bestScore)
{
bestScore = tempScore;
bestMove = attemptedMove;
}
}
}
}
return bestMove;
}
// TODO: Include alpha beta pruning
/// #brief The code looks 90% similar to getBestMove. However, one thing that differentiates them is it goes recursively into every single board for possibility.
/// #param board
/// #param depth How deep has the recursion gone
/// #param isMaximizing Checks which player turn. AI is usually maximizing. Player is usually minimizing.
/// #return The
int minimax(char board[3][3], int depth, bool isMaximizing)
{
// Remember that if we do not calculate how to get the results somewhere in this function,
// Return bestScore would be unable to run as there is no one returning a value.
char winResult = checkWinner(board);
if (winResult == X)
{
return (10 - depth);
}
else if (winResult == O)
{
return (-10 + depth);
}
else if (winResult == T)
{
return 0;
}
if (isMaximizing)
{
int bestScore = -1000;
for (int rows = 0; rows < 3; rows++)
{
for (int cols = 0; cols < 3; cols++)
{
struct Move attemptedMove = {rows, cols};
if (canMakeMove(board, attemptedMove)) // Check for available spot in the board.
{
char duplicateBoard[3][3];
copyBoard(board, duplicateBoard);
makeMove(duplicateBoard, attemptedMove, X);
// As long as the game doesn't end recursively call it till we get an end state.
int score = minimax(duplicateBoard, depth++, false);
if (bestScore < score)
{
bestScore = score;
}
}
}
}
return bestScore;
}
else
{
int bestScore = 1000;
for (int rows = 0; rows < 3; rows++)
{
for (int cols = 0; cols < 3; cols++)
{
struct Move attemptedMove = {rows, cols};
if (canMakeMove(board, attemptedMove))
{
char duplicateBoard[3][3];
copyBoard(board, duplicateBoard);
makeMove(duplicateBoard, attemptedMove, O);
// As long as the game doesn't end recursively call it till we get an end state.
int score = minimax(duplicateBoard, depth++, true);
if (bestScore > score)
{
bestScore = score;
}
}
}
}
return bestScore;
}
}
/// #brief Duplicates a board from originalBoard to duplicateBoard which creates a exact value but different array pointer
/// #param originalBoard
/// #param duplicateBoard
void copyBoard(char originalBoard[3][3], char duplicateBoard[3][3])
{
for (int rows = 0; rows < 3; rows++)
{
for (int cols = 0; cols < 3; cols++)
{
duplicateBoard[rows][cols] = originalBoard[rows][cols];
}
}
}
void MultiplayerMode()
{
char board[3][3] = {
{'0', '1', '2'},
{'3', '4', '5'},
{'6', '7', '8'},
};
bool gameInProgress = true;
int player = 0;
while (gameInProgress)
{
char playerChar = generatePlayerChar(player);
generateBoard(board);
printf("\nCurrently player %c turn.", playerChar);
printf("\nPlease enter your desired move: ");
struct Move desiredMove;
scanf(" [%d][%d]", &desiredMove.row, &desiredMove.column);
while (!canMakeMove(board, desiredMove))
{
printf("\nInvalid Move detected.");
printf("\nPlease re-enter your desired move: ");
scanf(" [%d][%d]", &desiredMove.row, &desiredMove.column);
}
makeMove(board, desiredMove, playerChar);
int result = checkWinner(board);
if (result == X)
{
printf("\nPlayer %c has won the game!", X);
gameInProgress = false;
}
else if (result == O)
{
printf("\nPlayer %c has won the game!", O);
gameInProgress = false;
}
else if (result == T)
{
printf("\nGame has resulted in a tie!");
gameInProgress = false;
}
player = !player;
}
}
void BotsMode()
{
char board[3][3] = {
{'0', '1', '2'},
{'3', '4', '5'},
{'6', '7', '8'},
};
bool gameInProgress = true;
int player = 0;
while (gameInProgress)
{
char playerChar = generatePlayerChar(player);
printf("\nCurrently player %c turn", playerChar);
if (player == 0)
{
struct Move desiredMove;
do
{
generateBoard(board);
printf("\nPlease enter your desired move: ");
scanf(" [%d][%d]", &desiredMove.row, &desiredMove.column);
} while (!canMakeMove(board, desiredMove));
makeMove(board, desiredMove, playerChar);
}
else
{
// This belongs to the AI.
struct Move bestMove = getBestMove(board);
printf("\nBest move determined is [%d][%d]", bestMove.row, bestMove.column);
makeMove(board, bestMove, playerChar);
}
int result = checkWinner(board);
if (result == X)
{
printf("\nPlayer %c has won the game!", X);
gameInProgress = false;
}
else if (result == O)
{
printf("\nPlayer %c has won the game!", O);
gameInProgress = false;
}
else if (result == T)
{
printf("\nGame has resulted in a tie!");
gameInProgress = false;
}
// Invert the player
player = !player;
}
}
int main(void)
{
int gameSelection;
do
{
printf("\nTic Tac Toe - Main Menu");
printf("\n1. Multiplayer Mode");
printf("\n2. Bots Mode");
printf("\nPlease enter your selection: ");
scanf("%d", &gameSelection);
} while (gameSelection != 1 && gameSelection != 2);
switch (gameSelection)
{
case 1:
MultiplayerMode();
break;
case 2:
BotsMode();
break;
}
return 0;
}
Hi there, I've tried and it seems to be working in certain combinations.
To use a move, i make use of [rows][cols]
So for the cases below, it doesn't seem to be working
Instruction 1: [0][2]
Instruction 2: [2][0]

Related

Why strcmp does not return 0?

I have a small program to handle a list of rabbits (name, district, participation count) stored in an array of pointers (Rabbit** iterator). I'd like to implement the following methods: add, delete and modify a rabbit, list all the rabbits or list by district.
When I compare for example the name of the rabbits in the list with strcmp() it doesn't return 0 when the names are equal. How can I solve this problem?
The Rabbit struct:
typedef struct Rabbit {
char* name;
char* district;
unsigned part_count;
} Rabbit;
The delete method:
bool delete_rabbit(char* delete_name)
{
for (unsigned i = 0; i < size; ++i) {
if (iterator[i] != NULL && strcmp(iterator[i]->name, delete_name) == 0) {
free(iterator[i]);
iterator[i] = NULL;
count--;
return true;
}
}
return false;
}
The list by district method:
void list_by_district(char* district)
{
unsigned counter = 0;
for (unsigned i = 0; i < size; ++i) {
if (iterator[i] != NULL && strcmp(iterator[i]->district, district) == 0) {
counter++;
printf("\n%u. Rabbit:\n", counter);
printf("Name: %s\nDistrict: %s\nParticipation count: %u\n", iterator[i]->name, iterator[i]->district, iterator[i]->part_count);
}
}
}
The modify method is similar to the delete method except it only changes the values.
The corresponding code snippets from main:
Rabbit** iterator;
unsigned size = 10, count = 0;
int main()
{
iterator = (Rabbit**)malloc(sizeof(Rabbit*) * 10);
...
do {
...
switch (input) {
case 'a':
if (count == size) iterator = allocate_more_memory();
...
iterator[count++] = add_rabbit(new_name, new_district, new_part_count);
break;
case 'd':
if (size == count + 6) iterator = allocate_less_memory();
do {
printf("Enter name to be deleted: ");
scanf("%[^\n]", delete_name);
getchar();
if (strlen(delete_name) >= 30) printf("Name only has 30 or less characters!\n");
} while (strlen(delete_name) >= 30);
if (!delete_rabbit(delete_name)) printf("\nThere's no rabbit in the list with this name.\n");
break;
...
}
} while (input != 'x');
...
free(iterator);
return 0;
}
EDIT:
The add method:
Rabbit* add_rabbit(char* new_name, char* new_district, unsigned new_part_count)
{
Rabbit* new_rabbit = (Rabbit*)malloc(sizeof(Rabbit));
if (new_rabbit) {
new_rabbit->name = (char*)malloc((strlen(new_name) + 1) * sizeof(char));
new_rabbit->district = (char*)malloc((strlen(new_district) + 1) * sizeof(char));
strcpy(new_rabbit->name, new_name);
strcpy(new_rabbit->district, district);
new_rabbit->part_count = new_part_count;
}
return new_rabbit;
}
The allocate less memory method:
Rabbit** allocate_less_memory()
{
Rabbit** new_iterator = (Rabbit**)malloc(sizeof(Rabbit*) * (size - 5));
if (new_iterator) {
unsigned counter = 0;
for (unsigned i = 0; i < size; ++i) {
if (iterator[i] != NULL) {
new_iterator[counter++] = iterator[i];
}
}
size -= 5;
free(iterator);
return new_iterator;
}
return NULL;
}

Peg solitaire solution with DFS gives segmentation fault

The Algorithm
Each possible configuration of the Peg Solitaire (Pegsol) is a tuple made of: m × m grid
board, the
position of the cursor and whether the peg under the cursor has been selected. This tuple is
called
a state. The Pegsol Graph G = <V, E> is implicitly defined. The vertex set V is defined as all
the
possible configurations (states), and the edges E connecting two vertexes are defined by the
legal jump
actions (right, left, up, down).
Your task is to find the path leading to the best solution, i.e. leading to the vertex (state) with
the
least number of remaining pegs. A path is a sequence of actions. You are going to use
Depth First
Search to find the best solution, up to a maximum budget of expanded/explored nodes
(nodes for
which you’ve generated its children).
When the AI solver is called (Algorithm 1 ), it should explore all possible paths (sequence of
jump
actions) following a Depth First Search (DFS) strategy, until consuming the budget or until a
path
solving the game is found. Note that we do not include duplicate states in the search. If a
state was
already generated, we will not include it again in the stack (line 21 ). The algorithm should
return the
best solution found, the path leading to the least number of remaining pegs. This path will
then
be executed by the game engine.
You might have multiple paths leading to a solution. Your algorithm should consider the
possible action by scanning the board in this order: traverse coordinate x = 0,....., m first, and
then y = 0, … , m looking for a peg that can jump, and then selecting jumping actions left,
right, up or down.
Make sure you manage the memory well. When you finish running the algorithm, you have
to free all
the nodes from the memory, otherwise you will have memory leaks. You will notice that the
algorithm
can run out of memory fairly fast after expanding a few million nodes.
When you applyAction you have to create a new node, that
points to the parent,
updates the state with the action chosen,
updates the depth of the node.
updates the action used to create the node
I'm implementing this algorithm with the following code:
#include <time.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include "ai.h"
#include "utils.h"
#include "hashtable.h"
#include "stack.h"
void copy_state(state_t* dst, state_t* src){
//Copy field
memcpy( dst->field, src->field, SIZE*SIZE*sizeof(int8_t) );
dst->cursor = src->cursor;
dst->selected = src->selected;
}
/**
* Saves the path up to the node as the best solution found so far
*/
void save_solution( node_t* solution_node ){
node_t* n = solution_node;
while( n->parent != NULL ){
copy_state( &(solution[n->depth]), &(n->state) );
solution_moves[n->depth-1] = n->move;
n = n->parent;
}
solution_size = solution_node->depth;
}
node_t* create_init_node( state_t* init_state ){
node_t * new_n = (node_t *) malloc(sizeof(node_t));
new_n->parent = NULL;
new_n->depth = 0;
copy_state(&(new_n->state), init_state);
return new_n;
}
/**
* Apply an action to node n and return a new node resulting from executing the action
*/
node_t* applyAction(node_t* n, position_s* selected_peg, move_t action ){
node_t* new_node = NULL;
//printf("PASS#");
//FILL IN MISSING CODE
new_node = create_init_node( &(n->state));
new_node->parent = n;
n->depth = n->depth + 1;
execute_move_t( &(new_node->state), &(new_node->state.cursor), action );
return new_node;
}
// From here my implementation starts
/**
* Find a solution path as per algorithm description in the handout
*/
void find_solution( state_t* init_state ){
HashTable table;
int8_t x,y;
int remainingPegs;
// Choose initial capacity of PRIME NUMBER
// Specify the size of the keys and values you want to store once
ht_setup( &table, sizeof(int8_t) * SIZE * SIZE, sizeof(int8_t) * SIZE * SIZE, 16769023);
// Initialize Stack
initialize_stack();
//Add the initial node
node_t* n = create_init_node( init_state );
node_t* new_node = NULL;
//FILL IN THE GRAPH ALGORITHM
stack_push(n);
remainingPegs = num_pegs(&(n->state));
while(!is_stack_empty())
{
n = stack_top();
expanded_nodes = expanded_nodes + 1;
if(num_pegs(&(n->state)) < remainingPegs )
{
save_solution(n);
remainingPegs = num_pegs(&(n->state));
}
for (x=0;x<SIZE;x++) {
for (y=0;y<SIZE;y++) {
enum moves_e a = left;
if(can_apply(&(n->state),&(n->state.cursor),a)){
printf("World cup");
node_t* new_node = applyAction(n,&(n->state.cursor),a);
generated_nodes = generated_nodes + 1;
//printf("%d",generated_nodes);
}
a = right;
if(can_apply(&(n->state),&(n->state.cursor),a)){
printf("World cup");
node_t* new_node = applyAction(n,&(n->state.cursor),a);
generated_nodes = generated_nodes + 1;
//printf("%d",generated_nodes);
}
a = up;
if(can_apply(&(n->state),&(n->state.cursor),a)){
printf("World cup");
node_t* new_node = applyAction( n,&(n->state.cursor),a);
generated_nodes = generated_nodes + 1;
//printf("%d",generated_nodes);
}
a = down;
if(can_apply(&(n->state),&(n->state.cursor),a)){
node_t* new_node = applyAction( n,&(n->state.cursor),a);
generated_nodes = generated_nodes + 1;
//printf("%d",generated_nodes);
}
//Comment line number 134 here where segmentation fault occured
if (won(&(new_node->state)))
{
//save_solution(new_node);
//remainingPegs = num_pegs(&(new_node->state));
//free_stack();
break;
}
if(new_node->depth == 0)
{
stack_push(new_node);
}
if(expanded_nodes >= budget)
break;
}
}
}
}
And another helper methods are present in util.c are mentioned below:
#include "utils.h"
#include "layouts.h"
void execute_move_t(state_t* state, position_s* selected_peg, move_t jump) {
int8_t x = selected_peg->x;
int8_t y = selected_peg->y;
switch (jump) {
case up: //Jump up
state->field[x][y-2] = 'o';
state->field[x][y-1] = '.';
state->field[x][y-0] = '.';
state->cursor.y = y-2;
break;
case down: //Jump down
state->field[x][y+0] = '.';
state->field[x][y+1] = '.';
state->field[x][y+2] = 'o';
state->cursor.y = y+2;
break;
case left: //Jump left
state->field[x-2][y] = 'o';
state->field[x-1][y] = '.';
state->field[x-0][y] = '.';
state->cursor.x = x-2;
break;
case right: //Jump right
state->field[x+0][y] = '.';
state->field[x+1][y] = '.';
state->field[x+2][y] = 'o';
state->cursor.x = x+2;
break;
}
}
bool can_apply(state_t *board, position_s* selected_peg, move_t jump){
//printf("%d",jump);
// Can select a Peg
if ( board->field[ selected_peg->x ][ selected_peg->y ] !='o') return false;
//Determine if move is legal
switch (jump) {
case up:
//printf("Up");
if( selected_peg->y < 2) return false;
if( board->field[ selected_peg->x ][ selected_peg->y - 1 ] !='o') return false;
if( board->field[ selected_peg->x ][ selected_peg->y - 2 ] !='.') return false;
printf("Up");
break;
case down:
//printf("Down");
if( selected_peg->y > SIZE - 3 ) return false;
if( board->field[ selected_peg->x ][ selected_peg->y + 1 ] !='o') return false;
if( board->field[ selected_peg->x ][ selected_peg->y + 2 ] !='.') return false;
printf("Down");
break;
case left:
//printf("Left");
if( selected_peg->x < 2) return false;
if( board->field[ selected_peg->x - 1 ][ selected_peg->y ] !='o') return false;
if( board->field[ selected_peg->x - 2 ][ selected_peg->y ] !='.') return false;
break;
case right:
//printf("Right");
if( selected_peg->x > SIZE - 3) return false;
if( board->field[ selected_peg->x + 1 ][ selected_peg->y ] !='o') return false;
if( board->field[ selected_peg->x + 2 ][ selected_peg->y ] !='.') return false;
printf("Right");
break;
}
// Can Jump
return true;
}
bool won(state_t *board) {
int8_t x,y;
int8_t count=0;
for (x=0;x<SIZE;x++) {
for (y=0;y<SIZE;y++) {
if (board->field[x][y]=='o') {
count++;
}
// If more than one peg is left, you haven't won yet
if( count > 1) return false;
}
}
//If only one is left
return count == 1;
}
int num_pegs( state_t *board ){
int count = 0;
for (int y=0;y<SIZE;y++) {
for (int x=0;x<SIZE;x++) {
count+=board->field[x][y]=='o';
}
}
return count;
}
void rotateBoard(state_t *board) {
int8_t i,j,n=SIZE;
int8_t tmp;
for (i=0; i<n/2; i++){
for (j=i; j<n-i-1; j++){
tmp = board->field[i][j];
board->field[i][j] = board->field[j][n-i-1];
board->field[j][n-i-1] = board->field[n-i-1][n-j-1];
board->field[n-i-1][n-j-1] = board->field[n-j-1][i];
board->field[n-j-1][i] = tmp;
}
}
i = board->cursor.x;
j = board->cursor.y;
board->cursor.x = -(j-n/2)+n/2;
board->cursor.y = (i-n/2)+n/2;
}
bool select_peg(state_t *board) {
int8_t x,y,(*field)[SIZE];
bool selected;
x = board->cursor.x;
y = board->cursor.y;
field = board->field;
selected = board->selected;
if (field[x][y]!='o') return false;
board->selected = !selected;
return true;
}
bool moveUp(state_t *board) {
int8_t x,y,(*field)[SIZE];
bool selected;
x = board->cursor.x;
y = board->cursor.y;
field = board->field;
selected = board->selected;
if (selected) {
if (y<2) return false;
if (field[x][y-2]!='.') return false;
if (field[x][y-1]!='o') return false;
if (field[x][y-0]!='o') return false;
field[x][y-2] = 'o';
field[x][y-1] = '.';
field[x][y-0] = '.';
board->cursor.y = y-2;
board->selected = false;
} else {
if (y<1) return false;
if (field[x][y-1]==' ') return false;
board->cursor.y = y-1;
}
return true;
}
bool moveLeft(state_t *board) {
bool success;
rotateBoard(board);
success = moveUp(board);
rotateBoard(board);
rotateBoard(board);
rotateBoard(board);
return success;
}
bool moveDown(state_t *board) {
bool success;
rotateBoard(board);
rotateBoard(board);
success = moveUp(board);
rotateBoard(board);
rotateBoard(board);
return success;
}
bool moveRight(state_t *board) {
bool success;
rotateBoard(board);
rotateBoard(board);
rotateBoard(board);
success = moveUp(board);
rotateBoard(board);
return success;
}
int8_t validMovesUp(state_t *board) {
int8_t x,y;
int8_t count=0;
for (x=0;x<SIZE;x++) {
for (y=SIZE-1;y>1;y--) {
if (board->field[x][y]=='o') {
if (board->field[x][y-1]=='o') {
if (board->field[x][y-2]=='.') {
count++;
}
}
}
}
}
return count;
}
bool gameEndedForHuman(state_t *board) {
int8_t i,count=0;
for (i=0;i<4;i++) {
count+=validMovesUp(board);
rotateBoard(board);
}
return count==0;
}
void initialize(state_t *board, int8_t layout) {
int8_t x,y;
if( layout > NUM_LAYOUTS - 1) layout = 0;
board->cursor.x = 4;
board->cursor.y = 4;
board->selected = false;
memset(board->field,0,sizeof(board->field));
for (y=0;y<SIZE;y++) {
for (x=0;x<SIZE;x++) {
board->field[x][y]=configuration[layout][y][x*2];
}
}
}
void drawBoard(state_t *board) {
int8_t x,y,count=0;
// move cursor to home position
printf("\033[H");
for (y=0;y<SIZE;y++) {
for (x=0;x<SIZE;x++) {
count+=board->field[x][y]=='o';
}
}
printf("peg-solitaire.c %7d pegs\n",count);
printf(" \n");
for (y=0;y<SIZE;y++) {
for (x=0;x<14-SIZE;x++) {
printf(" ");
}
for (x=0;x<SIZE;x++) {
if (board->cursor.x == x && board->cursor.y == y) {
if (board->selected) {
printf("\b|\033[7m%c\033[27m|",board->field[x][y]);
} else {
printf("\033[7m%c\033[27m ",board->field[x][y]);
}
} else {
printf("%c ",board->field[x][y]);
}
}
for (x=0;x<14-SIZE;x++) {
printf(" ");
}
printf("\n");
}
printf(" \n");
printf(" arrow keys, q or enter \n");
printf("\033[A"); // one line up
}
char* action_cstr(move_t move){
switch (move) {
case up:
return "Up";
break;
case down:
return "Down";
break;
case left:
return "Left";
break;
case right:
return "Right";
break;
}
return " ";
}
void print_solution(){
for(int i=0; i< solution_size; i++)
printf(" %d - %s \n", i+1, action_cstr(solution_moves[i]));
}
void play_solution(){
for(int i=0; i <= solution_size; i++){
drawBoard(&(solution[i]));
usleep(500000);
if( i < solution_size){
//Reverse action
switch ( solution_moves[i] ) {
case up:
solution[i].cursor.y = solution[i+1].cursor.y+2;
solution[i].cursor.x = solution[i+1].cursor.x;
break;
case down:
solution[i].cursor.y = solution[i+1].cursor.y-2;
solution[i].cursor.x = solution[i+1].cursor.x;
break;
case left:
solution[i].cursor.x = solution[i+1].cursor.x+2;
solution[i].cursor.y = solution[i+1].cursor.y;
break;
case right:
solution[i].cursor.x = solution[i+1].cursor.x-2;
solution[i].cursor.y = solution[i+1].cursor.y;
break;
}
solution[i].selected = true;
drawBoard(&(solution[i]));
usleep(500000);
}
}
}
It results in a segmentation fault. Please, anyone, help me to resolve this issue.

Why is the computer's input not being considered?

In the code below there is an error I can't locate causing the computer's selection to not be accounted for. The user's input is being considered in the Nim game yet the computer's pieces are not being subtracted.
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
void printInstructions();
int getUserInput(int);
int randomRange(int,int);
int PlayerTurn(int);
int smartCompMove(int);
int dumbCompMove(int);
int main()
{
srand(time(NULL));
int pieceAmount = randomRange(12,24);
char smartCompOrDumb;
printf("Do you want to play a smart computer or a dumb one? Select 'S' or 'D'.");
scanf("%c",&smartCompOrDumb);
bool gameOver = false;
bool playerTurn = true;
printInstructions();
while(pieceAmount > 0 && gameOver == false)
{
if (playerTurn == false)
{
if(pieceAmount <= 4)
{
printf("\nThe Computer has won :P ");
gameOver = true;
exit(0);
}
if (smartCompOrDumb == 's' || smartCompOrDumb == 'S')
{
playerTurn = true;
pieceAmount = smartCompMove(pieceAmount);
printf("%d",pieceAmount);
}
else if (smartCompOrDumb == 'd' || smartCompOrDumb == 'D')
{
playerTurn = true;
pieceAmount = dumbCompMove(pieceAmount);
printf("%d",pieceAmount);
}
}
else
{
if(pieceAmount <= 4)
{
printf("\nYou have won :) ");
gameOver = true;
exit(0);
}
playerTurn = false;
pieceAmount = PlayerTurn(pieceAmount);
printf("%d",pieceAmount);
}
}
return 0;
}
void printInstructions()
{
printf("\nThis game is called Nim and it is thousands of years old.");
printf("\nTake turns picking one to three pieces from a pile and whomever picks the last piece wins.");
printf("\n__________________________________________________________________________________________");
}
int randomRange(int low,int high)
{
return rand()% (high - low) + low;
}
int PlayerTurn(int pieceAmount)
{
pieceAmount = getUserInput(pieceAmount);
return pieceAmount;
}
int getUserInput(int pieceAmount)
{
int userInput = 0;
bool flag = true;
while (flag == true)
{
if (pieceAmount > 4)
{
printf("\nThere are %d pieces remaining.\n",pieceAmount);
printf("\nHow many pieces do you want to select? ");
scanf("%d", &userInput);
if (userInput >= 1 && userInput < 5)
{
pieceAmount = pieceAmount - userInput;
flag = false;
}
else
{
printf("This is not a valid move so try again.");
}
}
}
return pieceAmount;
}
int dumbCompMove(int pieceAmount)
{
int dumbPick = rand() % 3 + 1;
printf("\nComputer will pick from the stack. \n");
pieceAmount = pieceAmount - dumbPick;
printf("\nComputer picked %d pieces. \n", dumbPick );
return pieceAmount;
}
int smartCompMove(int pieceAmount)
{
int smartPick = 1;
printf("\nThe computer will select their pieces. \n");
if (pieceAmount >= 15 && pieceAmount < 24)
{
smartPick = 2;
pieceAmount = pieceAmount - smartPick;
}
else if (pieceAmount >= 10 && pieceAmount < 15)
{
smartPick = 4;
pieceAmount = pieceAmount - smartPick;
}
else if (pieceAmount >= 6 && pieceAmount < 10)
{
smartPick = 1;
pieceAmount = pieceAmount -smartPick;
}
else
pieceAmount = 3;
printf("\nThe computer selected %d pieces. \n",smartPick);
return pieceAmount;
}
I had this code working earlier yet somehow I must have altered something minor and now it will not function properly. I am using the Cloud9 program to run it.

MiniMax TicTacToe doesn't work (c)

I have implemented a TicTacToe algorythm with MiniMax, but the problem is that the computer always just places an 'x' on the next possible spot instead of evaluating the game. Does anybody know why ? (The problem can only be in the MiniMax function, or the nextMove function.)
Thanks a lot in advance !!
Here's the code :
int MiniMax(struct Game g, enum Symbol pl){
int score;
if (pl==CROSS)
{
score = -98765;
}
else score = 98765;
int temp_cross =0;
int temp_circle =0;
//base case
if (game_over(g) == CROSS)
return 10;
else if (game_over(g) == CIRCLE)
return -10;
else if (game_over(g) == FULL)
return 0;
int x,y;
for (y=0; y<SIZE_Y_AXIS; y++)
{
for (x=0; x<SIZE_X_AXIS; x++)
{
if (g.board.fields[x][y] == NONE)
{
if (pl == CROSS)
g.board.fields[x][y] = CROSS;
else g.board.fields[x][y] = CIRCLE;
if (pl == CROSS)
temp_cross= MiniMax(g, CIRCLE);
else temp_circle = MiniMax(g, CROSS);
g.board.fields[x][y] = NONE;
if ((pl == CROSS) && (temp_cross > score))
score = temp_cross;
else if ((pl == CIRCLE) && (temp_circle < score))
score = temp_circle;
}
}
}
return score;
};
int nextMove(struct Game g, enum Symbol player){
int score_cross = -865435;
int score_cross_temp = 0;
int cross_position = 1;
int score_circle = 876545;
int score_circle_temp = 0;
int circle_position = 1;
int x,y;
for (y=0; y<SIZE_Y_AXIS; y++)
{
for (x=0; x<SIZE_X_AXIS; x++)
{
if (g.board.fields[x][y] == NONE)
{
if (player == CROSS)
{
score_cross_temp = MiniMax(g, CROSS);
printf("%d ",MiniMax(g, CROSS));
if (score_cross_temp > score_cross)
{
score_cross = score_cross_temp;
cross_position = (y)*3 + x+1;
}
}
else if (player == CIRCLE)
{
score_circle_temp = MiniMax(g, CIRCLE);
if (score_cross_temp < score_circle)
{
score_circle = score_circle_temp;
circle_position = (y)*3 + x+1;
}
}
}
}
}
if (player == CROSS)
{
//printf("%d",cross_position);
return cross_position;
}
else
{
//printf("%d",circle_position);
return circle_position;
}
};
I believe there is a bug in your code. You initialize score as 10, and the temp variables as arbitrarily high and low numbers. This is backwards. TempCircle and TempCross are being overwritten anyway my the calls to minimax. It is the score variable that must be set like that. replace
int score = 10;
int temp_cross = -9876543;
int temp_circle = 9876543;
with
int score;
if(pl==cross){
score = 9876543
}
else{
score = -9876543
}
int temp_cross;
int temp_circle;
There also seems to be another bug in your nextMove function. I assume that its purpose is to iterate over all possible moves, find the one with the highest minimax value, and return that move (correct me if I'm wrong). That is not what the function does. It iterates over all the moves, but does't make any of them. x and y are never even used, except to update the move. You are essentially calling the same minimax several times. Instead, I would either get rid of the function completely, as there is a way to do this inside the minimax function, or fix this function. To fix the function, I would change it to this:
int nextMove(struct Game g, enum Symbol player){
int score_cross = -865435;
int score_cross_temp = 0;
int cross_position = 1;
int score_circle = 876545;
int score_circle_temp = 0;
int circle_position = 1;
int x,y;
for (y=0; y<SIZE_Y_AXIS; y++)
{
for (x=0; x<SIZE_X_AXIS; x++)
{
if (g.board.fields[x][y] == NONE)
{
if (player == CROSS)
{
g.board.fields[x][y] = CROSS;
score_cross_temp = MiniMax(g, CIRCLE);
printf("%d ",MiniMax(g, CROSS));
g.board.fields[x][y] = NONE;
if (score_cross_temp > score_cross)
{
score_cross = score_cross_temp;
cross_position = (y)*3 + x+1;
}
}
else if (player == CIRCLE)
{
g.board.fields[x][y] = CIRCLE;
score_circle_temp = MiniMax(g, CROSS);
g.board.fields[x][y] = NONE;
if (score_cross_temp < score_circle)
{
score_circle = score_circle_temp;
circle_position = (y)*3 + x+1;
}
}
}
}
}
if (player == CROSS)
{
//printf("%d",cross_position);
return cross_position;
}
else
{
//printf("%d",circle_position);
return circle_position;
}
};
Or you can edit minimax to keep track of which call to the function it is. If it is the first recursive call (the root) , keep track of the move itself as well as its value. Then return the move.

Tic Tac Toe resetting the board after the 3rd move

So everything works fine till the 4th move. It then resets the board and puts and X in the first box. What's wrong with it? Also I would prefer if I would be guided to the answer, not just given it, it's for the learning. I've also tried finding the answer without any help by cutting one part and checking what effect it does. (Don't know how to use the debugger cause it always gives out, filename has no debugging information).
#include <stdio.h>
#include <stdlib.h>
int clear(void);
int displayboard(void);
int initialize(void);
int startgame(void);
int checkwin(char);
int canwin(void);
int getmove(void);
int makemove(void);
int canblock(void);
char board[3][3];
int main()
{
int menuchoice;
while(1)
{
printf(" |\\ /| | | /\\ |\\ |\n");
printf(" | \\/ | | | /--\\ | \\ |\n");
printf(" | | | |_ / \\ | \\|\n");
printf(" Tic-tac-toe Version 1 \n\n");
printf(" [Main Menu]\n");
printf(" 1 - Start game\n");
printf(" 2 - Exit game\n");
scanf("%d",&menuchoice);
if(menuchoice==1)
{
clear();
startgame();
}
else if(menuchoice==2)
{
exit(EXIT_SUCCESS);
}
}
}
/* Prints newlines to refresh the page */
int clear(void)
{
int i;
for(i=0;i<25;i++)
{
printf("\n");
}
return 0;
}
/* Makes the board blank */
int initialize(void)
{
int numrow,numcol;
numrow=0;
numcol=0;
while(numrow<3)
{
while(numcol<3)
{
board[numrow][numcol]=' ';
numcol++;
}
numcol=0;
numrow++;
}
return 0;
}
/* Prints the board */
int displayboard(void)
{
printf(" 1 2 3\n\n\n");
printf(" 1 %c | %c | %c \n",board[0][0],board[0][1],board[0][2]);
printf(" ------------- \n");
printf(" 2 %c | %c | %c \n",board[1][0],board[1][1],board[1][2]);
printf(" ------------- \n");
printf(" 3 %c | %c | %c \n",board[2][0],board[2][1],board[2][2]);
return 0;
}
/* Function that controls the game*/
int startgame(void)
{
int movecount;
while(1)
{
movecount=0;
initialize();
displayboard();
board[1][1]='X';
clear();
displayboard();
getmove();
makemove();
movecount=3;
while(1)
{
clear();
displayboard();
getmove();
/* I believe the problem starts here */
if(checkwin('O')==1)
{
printf("\n\t\t\tYou WIN!!!!");
/*just to test*/
exit(EXIT_SUCCESS);
}
movecount++;
canwin(); /* Checks if it can make winning move */
if(canblock()==0) /*if a move to block has not been made*/
{
makemove();
movecount++;
}
else if(canblock()==1) /*if a move to block has been made*/
{
movecount++;
}
if(checkwin('X')==1)
{
printf("\n\t\t\tYou LOSE!!!");
/* just to test */
exit(EXIT_SUCCESS);
}
if(movecount==9)
{
printf(" Its a DRAW!!!");
/*Just to test */
exit(EXIT_SUCCESS);
}
}
}
}
/* Function to check win */
int checkwin(char ltr)
{
int rownumber,colnumber;
for(rownumber=0;rownumber<3;rownumber++)
{
if(board[rownumber][0]==ltr && board[rownumber][1]==ltr && board[rownumber][2]==ltr)
{
return 1;
}
}
for(colnumber=0;colnumber<3;colnumber++)
{
if(board[0][colnumber]==ltr && board[1][colnumber]==ltr && board[2][colnumber]==1)
{
return 1;
}
}
if(board[0][0]==ltr && board[1][1]==ltr && board[2][2]==ltr)
{
return 1;
}
else if(board[0][2]==ltr && board[1][1]==ltr && board[2][0]==ltr)
{
return 1;
}
else
return 0;
}
/* Function to get move */
int getmove(void)
{
int colnumber,rownumber;
printf("\n\t\t\tWhat row do you want to put your move?\n");
scanf("%d",&rownumber);
printf("\n\t\t\tWhat column do you want to put your move?\n");
scanf("%d",&colnumber);
board[rownumber-1][colnumber-1]='O';
return 0;
}
/* Function to makemove */
int makemove(void)
{
if(board[0][0]==' ')
{
board[0][0]='X';
return 0;
}
else if(board[0][2]==' ')
{
board[0][2]='X';
return 0;
}
else if(board[2][0]==' ')
{
board[2][0]='X';
return 0;
}
else if(board[2][2]==' ')
{
board[2][2]='X';
return 0;
}
else if(board[0][1]==' ')
{
board[0][1]=='X';
return 0;
}
else if(board[1][0]==' ')
{
board[1][0]='X';
return 0;
}
else if(board[1][2]==' ')
{
board[1][2]='X';
return 0;
}
else if(board[2][1]==' ')
{
board[2][1]='X';
return 0;
}
}
/* Function to make winning move */
int canwin(void)
{
int rownumber,colnumber;
for(rownumber=0;rownumber<3;rownumber++)
{
for(colnumber=0;colnumber<3;colnumber++)
{
board[rownumber][colnumber]='X';
if(checkwin('X')==1)
{
return 1;
}
board[rownumber][colnumber]=' ';
}
}
return 0;
}
/* Function to block winning move */
int canblock(void)
{
int rownumber,colnumber;
for(rownumber=0;rownumber<3;rownumber++)
{
for(colnumber=0;colnumber<3;colnumber++)
{
board[rownumber][colnumber]='O';
if(checkwin('O')==1)
{
board[rownumber][colnumber]='X';
return 1;
}
else if(checkwin('O')==0)
{
board[rownumber][colnumber]=' ';
}
}
}
return 0;
}
I think you got a problem in your canwin() function, as I understand it, you want the A.I. to test if it can make a winning move. But looking at the function
int canwin(void)
{
int rownumber,colnumber;
for(rownumber=0;rownumber<3;rownumber++)
{
for(colnumber=0;colnumber<3;colnumber++)
{
board[rownumber][colnumber]='X'; // <--- Problem here
if(checkwin('X')==1)
{
return 1;
}
board[rownumber][colnumber]=' ';
}
}
return 0;
}
You are actually overwriting current position of your board with an 'X', without checking if there already was a symbol there before! So you are essentially overwriting your board with 'X'es, checking if the X-player won and if not "resetting" to empty space ' '!
You can fix this by inserting a check before your write like this:
if (board[rownumber][colnumber] == ' ')
{
board[rownumber][colnumber]='X';
...

Resources