connect four - comparison of evaluation - c - c

I try to implement the minimax algorithm into my connect four game.
I´m done with the evaluation-function and halfway done with the algorithm-function.
I just can`t find the solution for the "last" problem. Here are my functions:
void minimax(field f){
int i;
field c;
convert_1D_to_2D(f, c);
for(i=0;i<COLS;i++) {
if(can_throw(c, i) == 0) {
throw(f, i);
convert_1D_to_2D(f, c);
if((is_winner(c) == 0) && (is_board_full(f) == 0)) { //no winner, board not full
minimax(f);
}
else if(is_winner(c) == 1) { //there is a winner
evaluate_turn(f);
//compare evaluation
undo_turn(f);
}
else if(is_winner(c) == 0 && (is_board_full(f) == 1)) { //no winner, board full
evaluate_turn(f);
//compare evaluation
undo_turn(f);
}
}
}
The field is an array with f[COLS*ROWS+1], where f[0] is the depth and the other elements save in which columns were thrown. the "c"-board represents the "graphical" board with 0 for free, 1 for player 1 and 2 for player 2.
static int evaluate_turn(field f) {
field c;
convert_1D_to_2D(f, c);
if (((f[0] % 2) == 1) && (current_player == 1) && (is_winner(c) == 1) ) { //player 1 won, max for him || +1
return 1;
}
else if (((f[0] % 2) == 2) && (current_player == 2) && (is_winner(c) == 1) ) { //player 2 won, max for him || +1
return 1;
}
if (((f[0] % 2) == 1) && (current_player == 2) && (is_winner(c) == 1) ) { //player 2 won, counting for 1 || -1
return -1;
}
else if (((f[0] % 2) == 2) && (current_player == 1) && (is_winner(c) == 1) ) { //player 1 won, counting for 2 || -1
return -1;
}
else if ((is_board_full(f) == 1) && (is_winner(c) == 0)) { //draw || 0
return 0;
}
So my problem is, that i can't think of a clean solution to compare the evaluation bottom to top. I really think, that I don't need to introduce a new datastructure (which would get way too big). It's like the solution is right in front of me but i can't grab it.
Is it possible to just compare the evaluation on the "way back" of the recursion? If yes, how?
Or do I really need to introduce something new more complex? Or maybe I'm missing off something completely?
Thanks!

Or do I really need to introduce something new more complex? Or maybe
I'm missing off something completely?
Unfortunately the answer is the latter. Minimax is not a void function. It returns the value of the node that it represents. That is how evaluation is compared. You are also missing another fundamental concept. Your function only considers terminal nodes to be those where the game is won or the board is full. While this is technically true, no real minimax function works that way. The number of nodes would be around 7^48, so your function would literally take upwards of ten years to terminate on a modern pc. What real world minimax functions do is set a maximum depth for the search to reach (unless you add tree pruning expect this to be 5 or 6), and consider all nodes at that depth to be terminal and evaluate them using a heuristic (inexact guess) evalation function. In connect four this could be based on something like the number of three in a rows. Another mistake you made is calling your eval function if you know there is a winner. If you know which player won than return the proper value straight out, no need to call the expensive eval function. You also cannot stream line your function for both min and max as you did. You must either create a seperate function for min and max, or use the negamax variant.
My advise: It seems you don't really understand how the algorithm should be implemented. Read up on minimax and negamax psuedocode.

Related

minimax - connect four - c - recursion

I try to implement the minimax-algorithm into my connect-four game.
I want to implement it with recursion. However I think i dont explore the whole gametree with my function:
for(i=0;i<COLS;i++) {
if(can_throw(c, i) == 0) {
throw(f, i);
if((is_winner(c) == 0) && (is_board_full(f) == 0)) { //no winner, board not full
minimax(f);
}
else if(is_winner(c) == 1) { //there is a winner
evaluate_turn();
undo_turn();
}
else if(is_winner(c) == 0 && (is_board_full(f) == 1)) { //no winner, board full
evaluate_turn();
undo_turn();
}
}
}
Does someone has a solution how to explore the whole gametree with recursion?
The evaluation is just 1 for win, 0 for draw, -1 for loss.
I`m not sure either whats the best way to compare the evaluations against each other, to have the right values on the "top-level" without introducing a datastructure (which would exceed memory). Does someone know how to do that either?

C program shows no error but doesn't show output

Roy wants to change his profile picture on Facebook. Now Facebook has some restriction over the dimension of picture that we can upload.
Minimum dimension of the picture can be L x L, where L is the length of the side of square.
Now Roy has N photos of various dimensions.
Dimension of a photo is denoted as W x H
where W - width of the photo and H - Height of the photo
When any photo is uploaded following events may occur:
If any of the width or height is less than L, user is prompted to upload another one. Print "UPLOAD ANOTHER" in this case.
If width and height are both large enough and
(a) if the photo is already square then it is accepted. Print "ACCEPTED" in this case.
(b) else user is prompted to crop it. Print "CROP IT" in this case.
Code:
#include <stdio.h>
#include <stdlib.h>
int main (void)
{
int len; /* len is the length of the side of square */
scanf("%d",&len);
int test;
scanf("%d",&test);
while(test--)
{
int w,h;
/* w - width of the photo and h- Height of the photo */
scanf("%d %d",&w,&h);
if(w==len && h==len)
{
printf("ACCEPTED\n");
}
else if(w>len || w==len && h>len || h==len)
{
printf("CROP IT\n");
}
else
{
printf("UPLOAD ANOTHER\n");/* print */
}
}
return 0;/* success */
}
If your problem is that you're seeing no output at all, it's likely because it's running in an IDE and the output window is closing before you can see it. Judicious use of a getchar() at the end of main() may be all you need to fix this, though I'd probably prefer to just run it from the command line.
In any case, since && has a higher precedence than ||, your second condition is effectively:
else if ((w > len) || (w == len && h > len) || (h == len))
which is clearly wrong since having a photo with width more than what was necessary would result in a request for cropping regardless of the other sub-conditions.
You would be better off following the textual specs more closely so that you can actually check it more easily. That would entail something like (with slightly simplified specs still meeting the original intent):
If any of the width or height is less than L, user is prompted to upload another one.
Otherwise both are large enough. If the photo is already square then it is accepted.
Otherwise user is prompted to crop it.
The code for that is a much simpler:
if ((w < len) || (h < len)) {
puts ("UPLOAD ANOTHER");
} else if (h == w) { // Both large enough otherwise previous
puts ("ACCEPTED"); // condition would have been true.
} else {
puts ("CROP IT");
}
Due to operator precedence,
else if(w>len || w==len && h>len || h==len)
is equivalent to:
else if(w>len || (w==len && h>len) || h==len)
What you need to use is:
else if( (w>len || w==len) && (h>len || h==len) && (w == h) )
You can simplify that to:
else if( w >= len && h >= len && w == h )
which can be further simplified to:
else if( w >= len && w == h )
It will show output but the program runs faster than you expect. It will close as soon as the output is displayed . It will not wait till you read it.
So use getch(); declared in conio.h to get a single key press as input after you have printed all the output (right before the return statement. It will make the program wait for the user to press a key before exiting.
here is the screenshot. it is running without error on me.
NOTE: This is not an answer. But this is just the only way to give picture example :) no need to up
Maybe you only need to put getchar(); at the end of the code before return to let the screen to pause for a while. it is just exiting very fast

If you have to put breaks in the if statements, why do we need to bother with conditions in the while operation?

This is just a general question really.
I wrote this originally
do
{
scanf("%i", &Carselect);
if (Carselect == 1)
{
mass = 1100;
velomax = 200;
}
else if (Carselect == 2)
{
mass = 1888;
velomax = 415
}
else if (Carselect == 3)
{
mass = 18000;
velomax = 129;
}
else
{
printf("Error in input. Please enter 1, 2 or 3.\n");
}
}
while (Carselect != 1 || Carselect != 2 || Carselect != 3);
And I got stuck in the loop. I put breaks in the statements for the valid conditions and that allowed me to get out, like this
do
{
scanf("%i", &Carselect);
if (Carselect == 1)
{
mass = 1100;
velomax = 200;
break;
}
else if (Carselect == 2)
{
mass = 1888;
velomax = 415;
break;
}
else if (Carselect == 3)
{
mass = 18000;
velomax = 129;
break;
}
else
{
printf("Error in input. Please enter 1, 2 or 3.\n");
}
}
while (Carselect != 1 || Carselect != 2 || Carselect != 3);
but I thought that the conditions for while were repeat conditions, so as soon as Carselect equals 1, 2 or 3 it will exit the loop. If you have to put breaks in the if statements, why do we need to bother with conditions in the while operation?
What is there, on the machine level or otherwise, that requires this seemingly trivial bit of logic?
Edit (as the question's title is changed):
The checking condition in the while/do-while loop is the one that is primarily checked to determine if the program is to stay in or to get out of the while/do-while loop - not the break statement.
The break statement is normally used if:
you want to get out of the while/do-while block before it is executing every statement in the block or
when certain exceptional termination condition is reached before you loop through your entire loop iteration or
You create an infinite loop and you capture an error which makes you unable to continue the loop or
Some other other cases which I might not be aware of...
Essentially, break-statement is not normally used to terminate the while/do-while block as long as the program follows (for lack of better term) "standard/main" path in the loop block.
In contrast, condition in the while/do-while loop is used to terminate the loop when the program follows "standard/main" path in the loop block.
Original:
You should change your inequality check (!=) into NOT equality check (==).
while (!(Carselect == 1 || Carselect == 2 || Carselect == 3));
This is because what you really want is for the loop to continue as long as
the Carselect is not (1 or 2 or 3)
Alternatively, you could change the or operator (||) to and operator (&&) which results in the same logic:
while (Carselect != 1 && Carselect != 2 && Carselect != 3);
the Carselect is not 1 and not 2 and not 3
In C break takes you to the statement immediately following } of the containing block, and continue takes you to to the statement immediately following { of the containing block. Both are useful in a long block, for example if an error condition occurs, or if a simpler input is detected, and not all the processing of the complete block is required, but you want to continue and get the next input.

+= and incremental growth give two different values in C

I have a chunk of code that is measuring the length of a block of text. I need the length of a gap of an unknown number of N inserted within the text (which consists of G, A, C, and T only) as well as the total length of the text block containing the gap(s). I am working one character at a time via a fgetc() and I devised two different ways to calculate the values, but they are giving me vastly differing results.
Method A:
...
} else if (in != '\n') {
scafLength++;
if (in == 'N') {
//Read entire gap
while ((in != 'G') && (in != 'A') && (in != 'C') && (in != 'T') && (in != '>')) {
if (in != '\n'){
gapLength++;
scafLength++;
}
in = fgetc (inFile);
//If it's at the end of the file
if (feof (inFile)) {
break;
}
}
...
Do stuff
...
//Reset for next gap and allow normal processing of next character after the gap
gapLength = 0;
fseek (inFile, -1, SEEK_CUR);
}
}
Method B:
...
} else if (in != '\n') {
scafLength++;
if (in == 'N') {
//Read entire gap
while ((in != 'G') && (in != 'A') && (in != 'C') && (in != 'T') && (in != '>')) {
if (in != '\n'){
gapLength++;
}
in = fgetc (inFile);
//If it's at the end of the file
if (feof (inFile)) {
break;
}
}
scafLength += gapLength - 1;
...
Do stuff
...
//Reset for next gap and allow normal processing of next character after the gap
gapLength = 0;
fseek (inFile, -1, SEEK_CUR);
}
}
> is a character used to denote a new scaf. This is the only area where scafLength and gapLength are altered, except to set them back to 0 for a new gap/scaf. Now, I expected an off-by-one in method A, which is why I made method B, but the numbers I'm getting are way off. Method A is giving me 16,777,216 as the longest scaf while Method B gives me 23,080,784. The real longest scaf (verified by multiple other programs) is 23,428,386 and there are not 5 million gaps to account for a fencepost error (the most is 180).
I'm curious why would these two approaches give such drastic differences? Also, if anyone can see a mistake I'm making, I'd appreciate it being pointed out.
EDIT: From the first few comments I need to clarify, the gapLength is reset to 0 after it serves it's purpose in the same else if (). I've updated the code.
In the first version it looks like you only incrementing scafLength when (in != '\n') but in the second version you continually add the value of gapLength - 1 whenever you enter the outer if-block and not just when you enter the inner (in != '\n')
Presumably the value of gapLength isn't reset and therefore the second version grows faster.
As your code is incomplete it's really hard to say, so consider this a guess.
Turns out the issue was with my variable type caused by my compiler. gapLength and scafLength were originally declared as int. Having run out of options, I switch them to long and the code performs perfectly. Apparently my compiler has the short int limit of 32,768.

How would I go about pausing the time (programming a game)

basically I need to implement a pause feature in my game (which is a simplified version of frogger) which stops the logs scrolling, and ignores any other input until the character p is pressed again). The way I've started to implement this in a while loop is to end it once another p Is pressed.
if(serial_input == 'p' || serial_input == 'P') {
while(1){
//need to pause the game
if(serial_input == 'p' || serial_input == 'P')
break;
}
This is how my logs are currently scrolling:
/* The following statements change the scrolling speeds of the individual logs */
current_time = get_clock_ticks();
if(is_frog_alive() && current_time >= last_move_time1 + 1000) {
scroll_lane(0, 1);
last_move_time1 = current_time;
} else if(is_frog_alive() && current_time >= last_move_time2 + 600) {
scroll_lane(1, -1);
last_move_time2 = current_time;
} else if(is_frog_alive() && current_time >= last_move_time3 + 800) {
scroll_lane(2, 1);
last_move_time3 = current_time;
} else if(is_frog_alive() && current_time >= last_move_time4 + 900) {
scroll_log_channel(0, -1);
last_move_time4 = current_time;
} else if(is_frog_alive() && current_time >= last_move_time5 + 1200) {
scroll_log_channel(1, 1);
last_move_time5 = current_time;
And this is implemented by a timer as described:
* We update a global clock tick variable - whose value
* can be retrieved using the get_clock_ticks() function.
*/
Any suggestions would be greatly appreciated
The best practice would depend on the libraries and general architecture you're using. That being said a naive implementation which I sometimes use would go somewhat like this:
while( playing) {
if( !paused) {
logic();
}
rendering();
input();
}
When doing small game projects, inside the main while loop I scatter logic, rendering and input in to different parts. In the input part there's the button that toggles the pause flag. In the main loop, the logic is simply enclosed in an if statement.
If you still need to do something inside the logic, you could pass it as a parameter or make it visible in some other way. Additionally you can do some special when-paused-graphics in the rendering section.
Details would vary but I hope this would at least give you a nudge to the right direction. That being said it is a common thing to implement and shouldn't be too hard to google.

Resources