Related
I am working on assignment. I want to create a game that is simulated and played by the computer. It is a logical game and the goal is to collect the most tokens with the highest value. Program starts by asking for input in this format('END' signals its the end of input, working on removing it):
N:1,2
W:3,5
E:9,1,1,1
S:1,7
END
Letters stand for direction like East, West, North, South. Numbers after the directions are tokens with value. You can only pick tokens from end of an direction. Player that collects most valuable tokens wins.
I need to make the game to play itself the most optimal way, found minimax algorithm. But I am completly clueless how to implement it correctly.
I am asking some kind soul, to help me make it work correctly. Give some tips at least :)
This is what I tried. Its working somehow but not the most optimal way. For example in the input I provided the most optimal result is:
A/B: 15/16
But I am getting:
A/B: 14/17
My code here.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#define MAX_TOKENS 100
struct Game
{
//lists with the values of the tokens on the individual arms of the cross
int *north;
int *west;
int *east;
int *south;
//lengths of token value lists
int north_size;
int west_size;
int east_size;
int south_size;
//scores of players A and B
int score_a;
int score_b;
};
void remove_token(struct Game *game, int player, char direction)
{
//variable for the token taken
int token;
//according to the direction, remove the token and add its value to the player's score
switch (direction)
{
case 'N':
//remove the token from the N arm
token = game->north[game->north_size - 1];
//reduce the length of the token value list by 1
game->north_size--;
//add the chip value to the player's score
if (player == 0)
game->score_a += token;
else
game->score_b += token;
break;
case 'W':
//remove the token from the W arm
token = game->west[game->west_size - 1];
//reduce the length of the token value list by 1
game->west_size--;
//add the chip value to the player's score
if (player == 0)
game->score_a += token;
else
game->score_b += token;
break;
case 'E':
//remove the token from the E arm
token = game->east[game->east_size - 1];
//reduce the length of the token value list by 1
game->east_size--;
//add the chip value to the player's score
if (player == 0)
game->score_a += token;
else
game->score_b += token;
break;
case 'S':
//remove the token from the S arm
token = game->south[game->south_size - 1];
//reduce the length of the token value list by 1
game->south_size--;
//add the chip value to the player's score
if (player == 0)
game->score_a += token;
else
game->score_b += token;
break;
default:
//throw an error if the player entered an invalid direction
printf("Neplatný směr pro odebrání žetonu!\n");
}
}
char minimax(struct Game *game, int depth, int player, int alpha, int beta)
{
//if all arms are empty or we have reached the maximum depth, return the best direction
if (game->north_size == 0 && game->west_size == 0 && game->east_size == 0 && game->south_size == 0 || depth == 0)
return 'X';
//the best move value for player A
int best_score_a = INT_MIN;
//the best move value for player B
int best_score_b = INT_MAX;
//direction for best move
char best_direction;
//go through all arms to find the best move
if (game->north_size > 0)
{
//copy the game state
struct Game game_copy = *game;
//remove the token from the N arm
remove_token(&game_copy, player, 'N');
//find out the best move for your opponent
int score = minimax(&game_copy, depth - 1, player == 0 ? 1 : 0, alpha, beta);
//update the best move for player A
if (player == 0 && score > best_score_a)
{
best_score_a = score;
best_direction = 'N';
}
//update the best move for player B
if (player == 1 && score < best_score_b)
{
best_score_b = score;
best_direction = 'N';
}
//update alpha and beta
if (player == 0)
alpha = fmax(alpha, score);
else
beta = fmin(beta, score);
//if beta is less than alpha, we end traversing the tree
if (beta <= alpha)
return best_direction;
}
if (game->west_size > 0)
{
//copy the game state
struct Game game_copy = *game;
//remove the token from the W arm
remove_token(&game_copy, player, 'W');
//find out the best move for your opponent
int score = minimax(&game_copy, depth - 1, player == 0 ? 1 : 0, alpha, beta);
//update the best move for player A
if (player == 0 && score > best_score_a)
{
best_score_a = score;
best_direction = 'W';
}
//update the best move for player B
if (player == 1 && score < best_score_b)
{
best_score_b = score;
best_direction = 'W';
}
//update alpha and beta
if (player == 0)
alpha = fmax(alpha, score);
else
beta = fmin(beta, score);
//if beta is less than alpha, we end traversing the tree
if (beta <= alpha)
return best_direction;
}
if (game->east_size > 0)
{
//copy the game state
struct Game game_copy = *game;
//remove the token from the E arm
remove_token(&game_copy, player, 'E');
//find out the best move for your opponent
int score = minimax(&game_copy, depth - 1, player == 0 ? 1 : 0, alpha, beta);
//update the best move for player A
if (player == 0 && score > best_score_a)
{
best_score_a = score;
best_direction = 'E';
}
//update the best move for player B
if (player == 1 && score < best_score_b)
{
best_score_b = score;
best_direction = 'E';
}
//update alpha and beta
if (player == 0)
alpha = fmax(alpha, score);
else
beta = fmin(beta, score);
//if beta is less than alpha, we end traversing the tree
if (beta <= alpha)
return best_direction;
}
if (game->south_size > 0)
{
//copy the game state
struct Game game_copy = *game;
//remove the token from the S arm
remove_token(&game_copy, player, 'S');
//find out the best move for your opponent
int score = minimax(&game_copy, depth - 1, player == 0 ? 1 : 0, alpha, beta);
//update the best move for player A
if (player == 0 && score > best_score_a)
{
best_score_a = score;
best_direction = 'S';
}
//update as soon as possible
if (player == 1 && score < best_score_b)
{
best_score_b = score;
best_direction = 'S';
}
//update alpha and beta
if (player == 0)
alpha = fmax(alpha, score);
else
beta = fmin(beta, score);
//if beta is less than alpha, we end traversing the tree
if (beta <= alpha)
return best_direction;
}
//return the best move
return player == 0 ? best_direction : best_direction;
}
void read_tokens(int *north, int *west, int *east, int *south, int *north_size, int *west_size, int *east_size, int *south_size)
{
//buffer for reading in input
char buffer[MAX_TOKENS];
//read in the input line by line
while (fgets(buffer, MAX_TOKENS, stdin) != NULL)
{
//remove the newline character from the end of the line
buffer[strcspn(buffer, "\n")] = 0;
//check for the "END" string to end the input
if (strcmp(buffer, "END") == 0)
break;
//split the line at the colon character
char *direction = strtok(buffer, ":");
char *tokens = strtok(NULL, ":");
//split the tokens at each comma
char *token = strtok(tokens, ",");
//determine the direction and store the tokens in the appropriate array
if (strcmp(direction, "N") == 0)
{
while (token != NULL)
{
north[*north_size] = atoi(token);
(*north_size)++;
token = strtok(NULL, ",");
}
}
else if (strcmp(direction, "W") == 0)
{
while (token != NULL)
{
west[*west_size] = atoi(token);
(*west_size)++;
token = strtok(NULL, ",");
}
}
else if (strcmp(direction, "E") == 0)
{
while (token != NULL)
{
east[*east_size] = atoi(token);
(*east_size)++;
token = strtok(NULL, ",");
}
}
else if (strcmp(direction, "S") == 0)
{
while (token != NULL)
{
south[*south_size] = atoi(token);
(*south_size)++;
token = strtok(NULL, ",");
}
}
else
{
//invalid direction = error
printf("Nespravny vstup.\n");
}
}
}
void print_progress(struct Game *game, int player, char direction)
{
char letter_player = ' ';
if (player == 0)
{
letter_player = 'A';
}
else
letter_player = 'B';
//printing of individual steps
switch (direction)
{
case 'N':
printf("%c: %c[%d] (%d)\n", letter_player, direction, game->north_size, game->north[game->north_size - 1]);
break;
case 'W':
printf("%c: %c[%d] (%d)\n", letter_player, direction, game->west_size, game->west[game->west_size - 1]);
break;
case 'E':
printf("%c: %c[%d] (%d)\n", letter_player, direction, game->east_size, game->east[game->east_size - 1]);
break;
case 'S':
printf("%c: %c[%d] (%d)\n", letter_player, direction, game->south_size, game->south[game->south_size - 1]);
break;
default:
break;
}
}
void play(struct Game *game, int depth)
{
//variable for current player (A or B)
int player = 0;
//until all chips are taken, alternate players taking chips
while (game->north_size > 0 || game->west_size > 0 || game->east_size > 0 || game->south_size > 0)
{
//player A
if (player == 0)
{
//function on the selection of a token
char direction = minimax(game, depth, 0, INT_MIN, INT_MAX);
print_progress(game, player, direction);
//remove the token from the game
remove_token(game, player, direction);
}
//player B
else
{
//function on the selection of a token
char direction = minimax(game, depth, 1, INT_MIN, INT_MAX);
print_progress(game, player, direction);
//remove the token from the game
remove_token(game, player, direction);
}
//switch players
player = (player + 1) % 2;
}
}
int main(void)
{
//field for token values
int north[MAX_TOKENS], west[MAX_TOKENS], east[MAX_TOKENS], south[MAX_TOKENS];
//sizes of token value fields
int north_size = 0, west_size = 0, east_size = 0, south_size = 0;
printf("Input:\n");
//fetch token values from input
read_tokens(north, west, east, south, &north_size, &west_size, &east_size, &south_size);
//creating a game
struct Game game;
game.north = north;
game.west = west;
game.east = east;
game.south = south;
game.north_size = north_size;
game.west_size = west_size;
game.east_size = east_size;
game.south_size = south_size;
game.score_a = 0;
game.score_b = 0;
//set the maximum depth of the minimax search tree
int depth = 1;
//start the game using the play() function
play(&game, depth);
//evaluation of the result of the game
printf("Celkem A/B: %d/%d\n", game.score_a, game.score_b);
return 0;
}
This is not an answer. The OP did ask for 'tips', though. Here are a few:
Don't drag-in the floating point library (math.h) for a single function ( fmax() ) that you could/should implement, especially as you are dealing with integer values and results.
Here are two 'branchless' statements returning the min or max of two integers: i & j.
int minVal = j ^ ((i^j) & -(i<j));
int maxVal = j ^ ((i^j) & -(i>j));
You can safely drop END from your data file. fgets() will find 4 lines and then return NULL, finishing the loading of the initial values.
As it stands, the initial values are single ASCII digits. Rather than calling atoi(), merely subtracting the ASCII character '0' from each ASCII digit will yield the (binary) integer value intended. (You might consider using 'A'-'Z' in the initial data, instead of '0' - '9', to both increase the range of points and obscure the best path to winning the game.
3a) Instead of "N:1,5,6,4", the data file could contain "N1564" obviating the need for using strtok()... Simply use each character in sequence.
It's quite understandable that you, as a beginner, have coded to the "literal" game involving 4 points of a compass. You've written code that would be very difficult to change to having 5 or 6 (or more) 'stack's of values to choose from.
If, instead of discrete variables named 'north...', 'west...', etc., had you used an array of 4 elements, most of the copy/paste/adapt that has bloated the source code's size would melt down to processing each of the 4 (or 5 or 6) 'stacks' in a loop. imo, the art of coding is balancing the drive to "make something work" with the necessity of code re-use wherever possible. Look for redundancy and strive to eliminate that.
Your code eliminates the trailing '\n' after fgets() has filled the input buffer. You then go on to translate an array of characters into arrays of discrete integer values. Then, the minimax function recusively trims copies of those arrays as it plays the game.
Consider keeping and working with the original array of characters (the string) instead. When "the user" picks the last 'character' (top of the stack) as their 'move', simply shorten the string of that stack. (Use C's string.h string handling capabilities instead of doing the work yourself with your own arrays of ints.) (The string "" has no more available tokens. Your code doesn't need to maintain a counter for each 'direction'.)
(This might be easiest.) When you've moved to using 4 arrays - one for each of 'N', 'W', 'E' and 'S' - you will see that the fixation of compass points has blinded you to using 'A', 'B', 'C' and 'D' instead... Given the "non-special" nature of "ABCD", it becomes a goal to write code that would work for "ABCDE" or "ABCDEF".
Summary: By replacing replicated code with re-used code, your program will shrink down to something easily manageable. When functionality is re-used and made 'general purpose', you will likely be able to improve the minimax processing. Time spent finding these 'improvements' will be re-paid in abundance.
Putting my money where my mouth is, below is the 28-line version of one of the OP's posted 59-line function. This still uses the discrete variables for each "compass direction". Were those bundled into an array, this, too, could be written in 1/2 again as many lines.
void remove_token( struct Game *game, int player, char direction ) {
int *arr, arr_sz;
//according to the direction, remove the token and add its value to the player's score
switch( direction ) {
case 'N':
arr = game->north;
arr_sz = --game->north_size;
break;
case 'W':
arr = game->west;
arr_sz = --game->west_size;
break;
case 'E':
arr = game->east;
arr_sz = --game->east_size;
break;
case 'S':
arr = game->south;
arr_sz = --game->south_size;
break;
}
//add the chip value to the player's score
if (player == 0)
game->score_a += arr[ arr_sz ];
else
game->score_b += arr[ arr_sz ];
}
I have made a (pretty bad) line follower.
Here is a sketch to roughly know the shape of the robot and location of the treads and sensors
[-] 0 0 [-] // 0 = color sensor
[-]------[-] // - = robot body
[-]------[-] // [-] = tank tread
[-] [-]
Here's what it does:
get Red, Green & Blue, make average of sensor 1 readings, do the same for 2
subtract to get value
this value will go through the PID part
steer with calculated steering
repeat (all of this is in a loop)
I use RGB and not reflected intensity (which is what is commonly used), because sometimes I need to detect if there's green color under the sensor (if there is, turn).
The real problem comes with the steering part. Unfortunately, it only accelerates a motor, meaning that in very tight turns we just lose the line.
Optimally, it should compensate a bit with the other motor (maybe going in the other direction?), but I am not sure how to calculate the speed of the motor, nor how to enforce this very strict line following policy.
Here is the code (I am also very grateful for any kind of tips on how to clean up the code! This is my first project in C :D ). I am not asking to read it all (it is pretty long), you could also just look at the steering function, and work your way back to rawFollowLine, this should hopefully shorten the code.
void rawFollowLine(int speed, float Kp, float Ki, float Kd){
_checkInit();
set_sensor_mode(sn_lx_color, "RGB-RAW");
set_sensor_mode(sn_rx_color, "RGB-RAW");
//printAllSensors();
int wasBlackCounter = 0;
int wasBlack = 0;
int lastBlack = 0;
for (int i = 0; i < 2000; i++)
{
if (isTerminating == 1)
{
killMotors(0);
break;
}
int greenCheck = rawGreenCheck(&wasBlack, &wasBlackCounter, &lastBlack);
if (wasBlack == 1){
wasBlackCounter++;
if (wasBlackCounter > 50){
wasBlackCounter = 0;
wasBlack = 0;
}
}
if (greenCheck == 1)
{
// lx is green
killMotors(1);
usleep(400 * 1000);
drive(200, 70);
waitIfMotorIsRunning();
killMotors(1);
pivotTurn(-90);
}
else if (greenCheck == 2)
{
// rx is green
killMotors(1);
usleep(400 * 1000);
drive(200, 70);
waitIfMotorIsRunning();
killMotors(1);
pivotTurn(90);
}
else if (greenCheck == 3)
{
// both rx and lx are green
killMotors(1);
turn(180);
}
else if (greenCheck == 5)
{
if(lastBlack == 2)
{
lastBlack = 0;
drive(100, -200);
//pivotTurn(50);
}
else if (lastBlack == 1)
{
lastBlack = 0;
drive(100, -200);
//pivotTurn(-50);
} else {
pidLineRaw(speed, Kp, Ki, Kd, &lastBlack);
}
}
else
{
pidLineRaw(speed, Kp, Ki, Kd, &lastBlack);
}
}
killMotors(1);
}
int rawGreenCheck(int *wasBlack, int *wasBlackCounter, int *lastBlack)
{
// Some documentation
// return nums:
// 3 = double green
// 2 = right green
// 1 = left green
// 0 = no green
int lx_red;
int lx_green;
int lx_blue;
int rx_red;
int rx_green;
int rx_blue;
get_sensor_value(0, sn_lx_color, &lx_red);
get_sensor_value(0, sn_rx_color, &rx_red);
get_sensor_value(1, sn_lx_color, &lx_green);
get_sensor_value(1, sn_rx_color, &rx_green);
get_sensor_value(2, sn_lx_color, &lx_blue);
get_sensor_value(2, sn_rx_color, &rx_blue);
//printf("rx_red %d\n", rx_red);
rx_red = (rx_red * rx_ratio_r);
rx_green = (rx_green * rx_ratio_g);
rx_blue = (rx_blue * rx_ratio_b);
//printf("rx_red (again) %d\n", rx_red);
if(
lx_red < 55 &&
lx_green > 90 &&
lx_blue < 55 &&
rx_red < 55 &&
rx_green > 90 &&
rx_blue < 55
)
{
// rx and lx see green
if (*wasBlack == 1)
{
// Apparently we crossed an intersection!
printf("Apparently we crossed an intersection!\n");
// We need to go straight.
*wasBlack = 0;
*wasBlackCounter = 0;
return 0;
}
else
{
return 3;
}
}
else if(lx_red < 55 && lx_green > 90 && lx_blue < 55)
{
// lx sees green
return 1;
}
else if(rx_red < 55 && rx_green > 90 && rx_blue < 55)
{
// rx sees green
return 2;
}
else if(rx_red < 50 && rx_green < 50 && rx_blue < 50 && lx_red < 50 && lx_green < 50 && lx_blue < 50)
{
// rx and lx see black
// this is needed if the intersection has the green tiles after the black line
printf("We are on the line? Is this an intersection?\n");
*wasBlack = 1;
return 0;
}
else if(lx_red < 55 && lx_green < 55 && lx_blue < 55)
{
// lx = right sees black
// this is needed if the intersection has the green tiles after the black line
//printf("We are on the line? Is this an intersection?\n");
killMotor(1, motor[R]);
rotateTillBlack(motor[L], sn_rx_color);
//printf("ASS2\n");
return 0;
}
else if(rx_red < 55 && rx_green < 55 && rx_blue < 55)
{
// rx = left sees black
killMotor(1, motor[L]);
rotateTillBlack(motor[R], sn_lx_color);
//printf("ASS1\n");
return 0;
}
//*lx_color_status = 0;
//*rx_color_status = 0;
*lastBlack = 0;
return 0;
}
void pidLineRaw(int speed, float Kp, float Ki, float Kd, int *lastBlack)
{
int red_lx_color;
int red_rx_color;
int green_lx_color;
int green_rx_color;
int blue_lx_color;
int blue_rx_color;
int lx_color;
int rx_color;
int last_error = 0;
int integral = 0;
int derivative = 0;
//float Kp = 0.1;
//float Ki = 0;
//float Kd = 0;
//set_sensor_mode(sn_lx_color, "COL-REFLECT");
//set_sensor_mode(sn_rx_color, "COL-REFLECT");
get_sensor_value(0, sn_lx_color, &red_lx_color);
get_sensor_value(0, sn_rx_color, &red_rx_color);
get_sensor_value(1, sn_lx_color, &green_lx_color);
get_sensor_value(1, sn_rx_color, &green_rx_color);
get_sensor_value(2, sn_lx_color, &blue_lx_color);
get_sensor_value(2, sn_rx_color, &blue_rx_color);
lx_color = (red_lx_color + green_lx_color+ blue_lx_color)/3;
rx_color = ( (red_rx_color*rx_ratio_r) + (green_rx_color*rx_ratio_g) + (blue_rx_color*rx_ratio_b))/3;
if(*lastBlack == 0)
{
int error = lx_color - rx_color;
integral = integral + error;
derivative = error - last_error;
last_error = error;
int steering_val = (error * Kp) + (integral * Ki) + (derivative * Kd);
// printf("error: %d\nsteering: %d\n",error, steering_val);
move_steering(-steering_val, speed, 1, 0);
} else if (*lastBlack == 1)
{
printf("lx_color_status\n");
move_steering(35, speed, 1, 0);
move_steering(-2, speed, 1, 0);
}
else if (*lastBlack == 2)
{
printf("rx_color_status\n");
move_steering(-35, speed, 1, 0);
move_steering(2, speed, 1, 0);
}
else
{
printf("HMMM: %d\n", *lastBlack);
exit(666);
}
}
static void _getSteeringSpeed(int speed, int *lx_speed, int *rx_speed, int steering)
{
if(steering > 100 || steering < -100)
{
printf("Yo wtf steering is %d\n", steering);
}
else
{
int speed_factor = (50 - abs(steering)) / 50;
*lx_speed = speed;
*rx_speed = speed;
if(steering >= 0)
{
*rx_speed = *rx_speed * speed_factor;
}
else
{
*lx_speed = *lx_speed * speed_factor;
}
}
}
Some parts are omitted, yes, they are not required to solve the problem.
I am also extremely sorry as there might be unused variables and such. I am working on refactoring the project, I'll update the post when I'm done.
So, summing everything up, I need to make sure that the steering part properly turns and follows the line. How do I do that? Is the code that I wrote even suitable? I'm guessing the steering itself might need some sort of feedback loop, to check if it's on the line?
I'm trying to use the DFS algo to create a maze in ASCII ('#' represents a wall and ' ' a free space) that has as start the upper left corner and as exit the lower right corner. The problem is the maze starts its creation and then it's blocked because all its neighbors have been already visited.
I start at the upper left corner, mark the cell as visited and put a ' ' (it represents a free space), then I chose randomly a neighbor of the cell and I do the same. However I put this in a while loop and I'm sure this isn't the good idea.
Here my attempt of the DFS :
int generation(t_maze *maze, int pos_y, int pos_x)
{
int dest;
maze->maze[pos_y][pos_x] = ' ';
maze->visited[pos_y][pos_x] = '1';
while (maze->maze[maze->height - 1][maze->width - 1] == '#')
{
if ((dest = my_rand(1, 4)) == 1 && pos_y - 1 >= 0 && maze->visited[pos_y - 1][pos_x] == '0')
generation(maze, pos_y - 1, pos_x);
else if (dest == 2 && pos_x + 1 < maze->width && maze->visited[pos_y][pos_x + 1] == '0')
generation(maze, pos_y, pos_x + 1);
else if (dest == 3 && pos_y + 1 < maze->height && maze->visited[pos_y + 1][pos_x] == '0')
generation(maze, pos_y + 1, pos_x);
else if (dest == 4 && pos_x - 1 >= 0 && maze->visited[pos_y][pos_x - 1] == '0')
generation(maze, pos_y, pos_x - 1);
my_showtab(maze->maze); //it prints the 2d array
usleep(50000);
}
typedef struct s_maze
{
int width;
int height;
char **maze;
char **visited;
} t_maze;
In the structure,
width is the width of the maze
height is the height of the maze
maze is a 2D array that is supposed to be filled with ' ' and '#'
visited is a 2D array with 0 and 1, 0 : unvisited, 1 : visited
I want to have a maze like this (little example)
########
# #
## #
# #
#######
Your code builds one path as it always goes to only one next cell. That's not a dfs. You could do it like that:
def dfs(x, y):
visited[x][y] = true
maze[x][y] = ' '
next_cell = random unvisited neighbors of (x, y):
dfs(next_cell.x, next_cell.y)
The point is: you need to backtrack at some point (it's convenient to use recursion for it). A single path won't look the way you want (it may also get stuck and never reach the exit).
I'm doing homework for UNI and I got to do a Tic-Tac-Toe without any decision taken by player, the moves are all chosen randomly. So if the character on matrix is ' ' it means it's free, while if it's 'X' or 'O' it should generate another move. This is the code (language C):
if (playerTurn == 1){
playerSymb = 'X';
}
else if (playerTurn == 2){
playerSymb = 'O';
}
if (matrix[rand1][rand2] == ' '){
matrix[rand1][rand2] = playerSymb;
} else if(matrix[rand1][rand2] == 'X' || matrix[rand1][rand2] == 'O'){
do{
randAlt1 = MINRND + rand()%(MAXRND - MINRND +1);
randAlt2 = MINRND + rand()%(MAXRND - MINRND +1);
}while (matrix[randAlt1][randAlt2] != 'X' && matrix[randAlt1][randAlt2] != 'O');
matrix[randAlt1][randAlt2] = playerSymb;
}
I did not copied the whole code because it's not finished at all, i just need help solving this. But if I try to run this, the Symbols can be overwritten, like if I have a 'X' at matrix[1][2], it's possible that it will be a 'O' after some turns. So how can I make moves do not overwrite? (sorry for bad english).
Just put correct condition:
while (matrix[randAlt1][randAlt2] == 'X' || matrix[randAlt1][randAlt2] == 'O')
(i.e. try again if this cell is not empty)
Also it is easy to simplify your code without loosing of anything:
randAlt1 = rand1;
randAlt2 = rand2;
while (matrix[randAlt1][randAlt2] != ' ') {
randAlt1 = MINRND + rand()%(MAXRND - MINRND +1);
randAlt2 = MINRND + rand()%(MAXRND - MINRND +1);
}
matrix[randAlt1][randAlt2] = (playerTurn == 1) ? 'X' : 'O';
And it is better to add loop guard to prevent infinite loop (or to add special checks for this case):
randAlt1 = rand1;
randAlt2 = rand2;
int nbAttempts = 0;
while (matrix[randAlt1][randAlt2] != ' ' && nbAttempts < 100) {
randAlt1 = MINRND + rand()%(MAXRND - MINRND +1);
randAlt2 = MINRND + rand()%(MAXRND - MINRND +1);
nbAttempts++;
}
if (matrix[randAlt1][randAlt2] != ' ') {
// show error message and stop the game
}
matrix[randAlt1][randAlt2] = (playerTurn == 1) ? 'X' : 'O';
You choose an arbitrary position and then test if it is free – possibly multiple times. But you can also choose a number of a free position and then find it.
First set up a turn counter
int turnNo = 0;
then make a loop for alternate moves, which chooses one of 9-turnNo unused positions, finds it, marks is with a player mark and tests if the move made a line of three:
while(turnNo < 9)
{
char currPlayerMark = ...choose 'X' or 'O';
int freePos = 9 - turnNo;
int currPos = rand() % freePos; // 0 .. freePos-1
for(x=0; x<3; x++)
{
for(y=0; y<3; y++)
{
if(matrix[x][y] == ' ') // a free position
if(--currPos < 0) // the sought one
break; // break the inner loop
}
if(currPos < 0)
break; // break the outer loop
}
matrix[x][y] = currPlayerMark;
if(test_for_win_position(x,y))
{
message_a_win_of_player(currPlayerMark);
break; // turnNo < 9 here
}
turnNo ++;
}
Finally test if the loop terminated with no 'win':
if(turnNo == 9)
message_its_a_draw(); // no-one wins
A function to test the win position might look like this:
int test_for_win_position(int x, int y)
{
char mark = matrix[x][y];
// check a column
if(matrix[x][0] == mark && matrix[x][1] == mark && matrix[x][2] == mark)
return 1;
// check a row
if(matrix[0][y] == mark && matrix[1][y] == mark && matrix[2][y] == mark)
return 1;
// check one diagonal
if(x==y)
if(matrix[0][0] == mark && matrix[1][1] == mark && matrix[2][2] == mark)
return 1;
// check another diagonal
if(x+y==2)
if(matrix[0][2] == mark && matrix[1][1] == mark && matrix[2][0] == mark)
return 1;
// current player has not won (yet)
return 0;
}
i am having a problem figuring out an algorithm for this problem,been trying for few days without success,here is a pic of what im trying to obtain:
http://i.stack.imgur.com/X70nX.png
Here is my code tried many differents solutions but always get stuck at the same point:(Sorry for mixed language the important part is in english)
ps
im not supposed to use functions to solve this problem only loops and array.
EDIT
after much fixing it does the walk but seldomly crashes
any idea?
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void){
char box[10][10];
int i,j;
int move,row,col;
char letter='A';
srand(time(NULL));
printf("\n\tSTART\n\n");
for(i=0;i < 10 ;i++)/* righe */
{
for(j=0;j < 10;j++) /* colonne */
{
box[i][j] = '.'; /* assegno . a tutti gli elementi dell array */
if(j == 9)
printf("%c%c\n", box[i][j]); /* giustifico ogni 10 elementi dell array j(0-9) */
else
printf("%c%c", box[i][j]);
}
}
/* LETS START */
printf("\n\n Inizia il gioco\n\n");
/* random place to start */
row = rand() % 9;
col = rand() % 9;
box[row][col]= 'A';
while(letter <= 'Z')
{
if(box[row+1][col] == '.' || box[row-1][col] == '.' || box[row][col+1] == '.' || box[row][col-1] == '.' )
{
move=rand() % 4;
switch(move){
case 0: /* Going UP */
if((row != 0) && (box[row-1][col] == '.'))
{
box[row-1][col]=++letter;
box[row--][col];
}else{
move=rand() % 4;
}
case 1:/* Going Down */
if((row != 9) && (box[row+1][col] == '.'))
{
box[row+1][col]=++letter;
box[row++][col];
}else{
move=rand() % 4;
}
case 2: /*Going Left */
if((col != 0) && (box[row][col-1] == '.'))
{
box[row][col-1]=++letter;
box[row][col--];
}else{
move=rand() % 4;
}
case 3: /* Going Right */
if((col != 9) && (box[row][col+1] == '.') )
{
box[row][col+1]=++letter;
box[row][col++];
}else{
move=rand() % 4;
}
}
}else{
printf("\n\nBloccato a %c\n\n", letter);
break;
}
}
/* FINE */
for(i=0;i<10;i++)/* righe */
{
for(j=0;j<10;j++) /* colonne */
{
if(j == 9)
printf("%c%c\n", box[i][j]); /* giustifico ogni 10 elementi dell array j(0-9) */
else
printf("%c%c", box[i][j]);
}
}
return 0;
}
You need to update row and col inside the loop.
Otherwise you'll always attempt to walk from the position of the 'A'.
... and once all 4 directions are filled, you're stuck in a infinite loop
. . . . .
. . B . .
. E A C .
. . D . .
Even when you update row and col inside the loop (and correct the == mistake), you have to handle a problem: suppose the first spot (the 'A') is the top left corner and the next random directions are East, South, South, West, and North. ... now what? :)
A B .
F C .
E D .
. . .
It's not a good idea to "reroll" the random number when you discover that you cannot go in some direction, because if you have bad luck, you get the same number twice (or even 3 or 4 or more times) - so even if you generated 4 random numbers and they all failed, that doesn't mean that you're stuck.
You can solve this problem by generating one number, and trying all 4 possible directions starting from it:
If the random number generator returned 0: check 0, 1, 2, 3
If the random number generator returned 1: check 1, 2, 3, 0
If the random number generator returned 2: check 2, 3, 0, 1
If the random number generator returned 3: check 3, 0, 1, 2
Implemented by the following code:
desired_move = rand();
success = 0;
for (i = 0; i < 4 && !success; ++i)
{
move = (desired_move + i) % 4;
switch (move)
{
case 0: // Go up
if (row > 0 && box[row - 1][col] == '.')
{
row = row - 1;
success = 1;
}
break;
case 1: // Go down
...
}
}
if (!success) // Tried all 4 directions but failed! You are stuck!
{
goto START_OVER; // or whatever else
}
Note that this algorithm is not very random: if you cannot go up, there is a greater chance that you go down than right or left. If you want to fix it, you can pick a random permutation of 4 directions instead of checking the directions sequentially:
const int permutation_table[24][4] = {
{0, 1, 2, 3},
{0, 1, 3, 2},
{0, 2, 1, 3},
...
{3, 2, 1, 0}
};
index = rand() % 24;
for (i = 0; i < 4; ++i)
{
move = permutation_table[index][i];
switch (move) {
... // As above
}
}
When you're in for loop.
Draw a possible direction
int direction = rand()%4;
Check all possible directions if the drawed one is invalid (not in array or not a ".")
int i=-1;
while( ++i < 4 )
{
switch(direction)
{
case 0:
if( row-1 >= 0 && box[row-1][col] == '.' ) {
--row;
i = -1;
}
break;
case 1:
if( col+1 < 10 && box[row][col+1] == '.' ) {
++col;
i = -1;
}
break;
case 2:
if( row+1 < 10 && box[row+1][col] == '.' ) {
++row;
i = -1;
}
break;
case 3:
if( col-1 >= 0 && box[row][col-1] == '.' ) {
--col;
i = -1;
}
break;
}
if( i != -1 ) {
direction = (direction+1)%4;
}
else {
break;
}
}
If there's no valid move end the for loop>
if( i == 4 ) {
break;
}
Otherwise write a letter to the table cell and update row/col position.
box[row][col] = letter;
And... that's all I guess. This is greedy algorithm so you don't need any optimizations (at least I don't see any in exercise requirements.
It looks like you are breaking out of your switch statement if you try to go in a direction that isn't valid, but you increment your counter anyway. Try to check another random direction if that happens.
where exactly does it break?
from what I can see at a glance is that you have a chance that It_that_walks gets in position from witch it cant go anywhere:
A B C D .
. I J E .
. H G F .
where after J?
There is no need for the && (box[row][col-1]= '.')
Allso, it is wrong (assignment instead of comparison), it should be: && (box[row][col-1]== '.') (but you dont need it alltogether)