I am writing a Connect 4 game with minimax, but my next-step checking function is sometimes giving me unexpected results. Can you please tell me if my algorithm is correct?
For example if my board looks like this
0000000
0000000
0000000
0000000
1000000
2002120
it will return column 0 as true for player 2.
bool Board::check2(int player, int& bestMove)
{
for (int i=0; i<WIDTH; i++)
{
if(addToColumn(i, player))
{
if (checkNext(2, i, player))
{
bestMove=i;
removeFromColumn(i, player);
return true;
}
removeFromColumn(i, player);
}
}
return false;
}
bool Board::checkNextVertical(int size, int column, int player1)
{
int counter=0;
int player2;
if (player1==1)
{
player2=2;
}
else
player2=1;
for (int i=0 ; i<DEPTH; i++)
{
if (arrBoard[column][i]==player1)
{
counter++;
}
if (arrBoard[column][i]==player2)
{
return false;
}
if (counter==size)
{
return true;
}
}
return false;
}
bool Board::checkNextHorizontal(int size, int column, int player1)
{
int counter=0;
int player2;
if (player1==1)
{
player2=2;
}
else
player2=1;
for (int i=0 ; i<DEPTH; i++)
{
if (arrBoard[i][column]==player1)
{
for (int j = 0; j<WIDTH; j++)
{
if (arrBoard[i][j]==player1)
{
counter++;
}
if (arrBoard[i][j]!=player1)
{
counter=0;
}
if (counter==size)
{
return true;
}
}
}
}
return false;
}
bool Board::checkNext(int size, int column, int player)
{
if (checkNextVertical(size, column, player))
{
// printBoard();
return true;
}
if (checkNextHorizontal(size, column, player))
{
// printBoard();
return true;
}
return false;
}
Welcome to the forum.
There are a few problems with the code you posted:
Your checkNextVertical function appears to be attempting to check horizontally, and your checkNextHorizontal function appears to attempting to check both horizontally and vertically.
If you notice you use both arrBoard[column][i] and arrBoard[i][column]. I'm sure you'll agree only one of these can be correct. It's important to understand which is correct, or else your code will end up attempting to access locations in the array which are not valid, and you will get unexpected behaviour, for example your j loop in the checkNextHorizontal function is currently doing this.
It should be used as array[y / depth / row][x / width / column] - or whatever you will remember.
Personally, this code seems confusing:
int player2;
if (player1==1)
{
player2=2;
}
else
player2=1;
player2=1 seems like trying to push a square peg in a round hole. Could you use int player and set it to either 1 or 2 to make it easier to read?
I totally agree with Joachim - if you have these sorts of problems, it's always a great idea to fill the array with some data, then use the debugger to step through your code and check that the data being accessed is the data you expect to be accessed.
Alternatively, since it's a connect4 game, I assume at some point you know the column which the last move was made, in which case you can use this function to check if it was a winning move. You just need to tell it which column the last move was, and the required 'size' to win. If you do use it, I would still recommend stepping through it with the debugger so you can understand the array access. NB: your code wasn't checking diagonally - so neither does this. Some extra logic required if you want to do that:
bool winningMove(int column, int size)
{
bool winnerWinnerChickenDinner = false;
int player = 0;
int row = 0;
// Who was the last player to go in this column
// i.e. find the top non-zero entry
for (int i = 0; i < DEPTH; i++)
{
if (arrBoard[i][column] != 0)
{
player = arrBoard[i][column];
row = i;
break;
}
}
// If we found a player, check if it was a winning move
if (player != 0)
{
int count = 0;
// Loop twice, first horizontally, then vertically
for (int i = 0; i < 2 && !winnerWinnerChickenDinner; i++)
{
bool horizontal = (i == 0);
for (int j = 0; j < (horizontal ? WIDTH : DEPTH); j++)
{
// Check if we have 'size' consecutive entries by the same player
// (When we check horizontally, use arrBoard[row][j] to check the row)
// (When we check vertically, use arrBoard[j][column] to check the column)
if (arrBoard[(horizontal ? row : j)][(horizontal ? j : column)] == player)
{
if (++count == size)
{
winnerWinnerChickenDinner = true;
break;
}
}
else
{
count = 0;
}
}
}
}
return winnerWinnerChickenDinner;
}
The old games are the best - Connect4 is awesome, so good luck.
Related
I wrote a function to find all combinations of integers between two bounds. To do this, I wrote a function with the same name that finds all combinations of integers between two bounds of a certain size.
In main, I set up a loop to call this function multiple times. When it is ran more than once with bounds that are sufficiently far apart, it causes an error, that is, the code reaches the logic error in the first function.
I do not know why multiple passes in the while-loop causes a problem because the variables should be reset each time.
#include <vector>
#include <stdexcept>
#define VAR1 3
#define VAR2 8
bool nextCombination(std::vector<int> &combo, int numItems, \
int lowerBound, int upperBound) {
if (combo.empty()) { //This is the first take.
for (int i = 0; i < numItems; ++i) {
combo.push_back(lowerBound + i);
}
return true;
} else if (combo[0] >= upperBound - numItems) { //This cleans up.
combo.clear();
return false;
} else {
for (int i = 0; i < numItems; ++i) {
if (i >= numItems || combo[i]+1 != combo[i+1]) {
++combo[i]; //This extends the front of the stack.
return true;
} else { //This pushes the first part of the stack back.
combo[i] = lowerBound + i;
}
}
}
throw std::logic_error("There is an error with nextCombination.");
}
bool nextCombination(std::vector<int> &combo, int lowerBound, int upperBound) {
int numItems = combo.size();
if (numItems >= upperBound - lowerBound) {
combo.clear();
return false;
} else if (numItems == 0) {
combo.push_back(0);
return true;
} else {
if (nextCombination(combo, numItems, lowerBound, upperBound)) {
return true;
} else {
combo.clear(); //This line shouldn't be needed.
return (nextCombination(combo, numItems+1, lowerBound, upperBound));
}
}
}
int main() {
for (int i = 0; i < VAR1; ++i) {
std::vector<int> combo;
while (nextCombination(combo, 0, VAR2)) ;
}
return 0;
}
I don't understand the algorithm, but the problems are clear enough.
Step one, reduce the size of the problem I chose
#define VAR1 1
#define VAR2 2
Run the code, crash in operator[] here,
if (i >= numItems || combo[i]+1 != combo[i+1])
Values of variables
combo = vector of size 1
numItems = 1
i = 0
combo[i+1] is a subscript out of bounds error.
It doesn't take long to step through the code to get to this point. It happens immediately on the second iteration of the inner loop in main. Since I don't understand what your code is trying to do I can't suggest a fix. But hopefully the error is clearer to you now.
Since it seems you were unaware of this subscripting issue, you should change to using at instead of [] that way you get defined behaviour even on a subscript error.
This is the link to the problem here
We have to tell the order in which the letters(like bricks) have to be laid in order for the wall to be stable. ie; for example if the input is
ZOAAMM
ZOAOMM
ZOOOOM
ZZZZOM
Then if I lay 'A's first then they will fall to the ground since in order to lay the letter 'A' I need to lay the letters 'Z' and 'O' first.
If I lay all O's first then they will also fall on the ground since some of the O's depend on the 'Z' below to be laid first.
The answer for the above is 'ZOMA' or 'ZOAM' both are valid answers. If it's not possible then we should print -1.
What I did in my code is that I first made a graph. For the example above, the graph will be O-> A since A depends on O. O->M since M also depends on O. Z->O since O depends on Z.
Then if there is a cycle in the graph then that means it is not possible to lay the wall so I print -1
If there is no cycle then the answer exists, and to get the answer I do a topological sort.
All the example test cases passed, but when I submit, I get a wrong answer. I don't know which test case could be failing. Can you let me know any case which my code is failing for please.
#include<bits/stdc++.h>
using namespace std;
vector<int>adj[26]; // adjacency list to store the graph
int n,m; // n rows each with a string of size m
string s[30];
bool vis[26]; //for topological sort
int visited[26];//for cycle detection
stack <char> mystack;
bool flag = 0;
void cycledfs(int node) // returns -1 if cycle detected
{
if(visited[node] == -1)
{
flag = 1;
return;
}
visited[node] = -1;
for(int i = 0; i < adj[node].size(); i++)
{
if(visited[adj[node][i]] != 1)
{
cycledfs(adj[node][i]);
}
}
visited[node] = 1;
}
void dfs(int node) // if cycle is not detected we do the topological sorting
{
if(!vis[node])
{
vis[node] = 1;
for(int i = 0; i < adj[node].size(); i++)
{
if(!vis[adj[node][i]])
dfs(adj[node][i]);
}
mystack.push(node+'A');
}
}
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
int t;
cin>>t;
while(t--)
{
cin>>n>>m;
set <char> t; // set to store all the unique letters which are my vertices of the graph
for(int i = 0; i <n; i++)
{
cin>>s[i];
for(int j = 0; s[i][j]!='\0';j++)
t.insert(s[i][j]);
if(i)
{
for(int j = 0; s[i][j]!='\0';j++)//to check if the brick above me is different from me. Also making sure no duplicates in the graph.
{
auto it = find(adj[s[i][j]-'A'].begin(), adj[s[i][j]-'A'].end(), (s[i-1][j]-'A'));
if(((s[i][j]-'A') != (s[i-1][j]-'A')) &&(it==adj[s[i][j]-'A'].end()))
adj[s[i][j]-'A'].push_back((s[i-1][j]-'A'));
}
}
}
//initializing stuff
flag = 0;
memset(visited, 0, sizeof(visited));
memset(vis, 0, sizeof(vis));
for(char i: t)//CYCLE CHECKING
{
//cout<<(i-'A')<<"\n";
if(visited[i-'A'] == 0)
cycledfs(i-'A');
if(flag)
break;
}
if(flag)
cout<<"-1\n";
else //doing topological sort if no cycle
{
string result ="";
for(char x: t)
dfs(x-'A');
while(!mystack.empty())
{
char ans = mystack.top();
result.push_back(ans);
mystack.pop();
}
cout<<result<<"\n";
}
for(int i = 0; i < 26; i++) //clearing adj list
adj[i].clear();
}
}
Editor is saying error: Out of memory. Hi guys, I am learning about the sorted list. The method PrintMessage runs every second. And the Add funtion is causing the bug. Would you be able to tell what's wrong based on the code blow? Thank you.
void PrintMessage(GameObject gameObject) {
Target newTarget = new Target(gameObject, transform.position);
targets.Add(newTarget);
print(targets[targets.Count-1].Distance.ToString());
}
public void Add(T item)
{
int num;
// add your implementation below
if (items.Count != 0)
{
for (int i = 0; i < items.Count; i++)
{
num = item.CompareTo(items[i]);
if (num >= 0)
{
tempList.AddRange(items.GetRange(i, items.Count - i));
items.RemoveRange(i, items.Count - i);
items.Add(item);
items.AddRange(tempList);
tempList.Clear();
continue;
}
}
items.Add(item);
}
else
{
items.Add(item);
}
}
The problem is that inside of
for (int i = 0; i < items.Count; i++)
{
...
items.Add(item);
...
}
you continously add more and more items. So every iteration of the loop the items.Count will be +1 item bigger => the exit condition i >= items.Count will never be met.
→ Never change the List count while iterating over the same list!
Reason for that in the end is you're using continue(go to the next iteration) .. it makes no sense there since at this point anyway the next iteration would start.
You probably ment break(interrupt the loop) or even return since anyway right after the loop again you call items.Add(item) ...
You probably rather want to use List<T>.Insert(int index, T item)
public void Add(T item)
{
int newIndex = 0;
// You don't need an additional if-else
// since this loop is anyway never executed
// if (items.Count == 0)
for (int i = 0; i < items.Count; i++)
{
num = item.CompareTo(items[i]);
if (num >= 0)
{
// we want to add the new item AFTER
// the item we compared it to
newIndex = i+1;
return;
}
}
// Inserts the item at index newIndex
// if newIndex == items.Count this equals Add
items.Insert(newIndex, item);
}
Note that this actually already exists!
It is called SortedSet<T>
I have a loop that looks for 3 equal cards or 3 non equal cards and erases as it finds, if it doesn't find 2 equals/non-equals for the 1st chosen element it deletes that 1st element and goes to the other and so on...
Well, I'm using goto in this code to break from inside of two for loops and keep iterating throughout while.
To me, it makes good sense to use goto in this specific situation. But, since I'm not a very experienced programmer I think there would be a better way to do it, a more efficient way.
Is there? How would that be? not using goto in this case.
unsigned int i1 = 0;
while(gameCards.size() > 2)
{
for(unsigned int i2=1; i2<gameCards.size(); i2++)
{
if(i2 == 2) continue;
if(cannotMatch(gameCards.at(i1), gameCards.at(i2)))
{
for(unsigned int i3=2; i3<gameCards.size(); i3++)
{
if(cannotMatch3(gameCards.at(i1), gameCards.at(i2), gameCards.at(i3)))
{
SetMatches++;
gameCards.erase(gameCards.begin()+i2,gameCards.begin()+i3);
goto findAnother;
}
}
} else if(canMatch(gameCards.at(i1), gameCards.at(i2)))
{
for(unsigned int i3=2; i3<gameCards.size(); i3++)
{
if(canMatch3(gameCards.at(i1), gameCards.at(i2), gameCards.at(i3)))
{
SetMatches++;
gameCards.erase(gameCards.begin()+i2,gameCards.begin()+i3);
goto findAnother;
}
}
}
}
findAnother:
gameCards.erase(gameCards.begin()+(i1++));
}
You can just set an extra bool condition to break outer for loop. You can also simplify your inner loops when you notice that they are essentially the same, just invoke different match3 functions:
while(gameCards.size() > 2)
{
auto continue_outer_loop(true);
for(unsigned int i2=1; continue_outer_loop && (i2<gameCards.size()); i2++)
{
if(i2 == 2) continue;
auto const p_match_3_func
(
cannotMatch(gameCards.at(i1), gameCards.at(i2))
?
&cannotMatch3
:
&canMatch3
);
for(unsigned int i3=2; i3<gameCards.size(); i3++)
{
if((*p_match_3_func)(gameCards.at(i1), gameCards.at(i2), gameCards.at(i3)))
{
SetMatches++;
gameCards.erase(gameCards.begin()+i2,gameCards.begin()+i3);
continue_outer_loop = false;
break;
}
}
}
gameCards.erase(gameCards.begin()+(i1++));
}
You can add guard variable and check for it in your loops. But will work, only when you do it at the end of loop as it is not real break.
while (mainLoop) {
int goMain = 0;
for (int i = 0; i < 5 && goMain == 0; i++) {
for (int j = 0; j < 5 && goMain == 0; j++) {
if (wantExit) {
goMain = 1;
}
}
}
}
I have to do an exercise for University that asks me to check ( for k times ) if a matrix has positive rows ( i mean a row with all positive elements ) , i think there's something wrong with the indices of for loops but i cannot find the mistakes.
i tried to debug with a cout statement apply to the counter an it gives me "101" , so it seems like compiler assign "1" to the positive rows and "0" to the negative
This is the code i wrote:
#include <iostream>
using namespace std;
const int N = 3;
bool positive(int a[N][N], int row[N], int x) {
bool condition = false;
for(int i = 0; i < N; i++) {
row[i] = a[x][i];
}
int j = 0;
while(j < N) {
if(row[j] >= 0) {
condition = true;
} else {
condition = false;
}
j++;
}
return condition;
}
bool function (int a[N][N], int z, int j, int k) {
int b[N];
int c[N];
int count = 0;
if(positive(a, b, z) && positive(a, c, j)) {
count++;
}
if(count == k) {
return true;
} else {
return false;
}
}
int main() {
int a[N][N] = {
{
2, 8, 6
}, {
-1, - 3, - 5
}, {
6, 10, 9
}
};
int k = 2;
for(int i = 0; i < N; i++) {
for(int j = 0; j < N; j++) {
if(function (a, i, j, k)) {
cout << "OK";
} else {
cout << "NO";
}
}
}
return 0;
}
You should probably take another look at this problem and restart with a different solution. The objective is pretty easy but your code is surprisingly complex and some of it doesn't really make sense.
For example, if you had a matrix like this:
1 2 4 --> matrix A
-1 8 -6
3 9 2
You have N=3 rows and columns. The only thing you have to to based on what you said is take the matrix, cycle through the N rows, and for each row, check it's N columns to see if anything is < 0.
Doing this K times, as you put it, makes no sense. The matrix will be the same every time you compare it since you're not changing it, why would you do it more than once? I think you should reread the assignment brief there.
As for the logic of finding which rows are positive or negative, just do something simple like this.
//Create array so we have one marker for each row to hold results.
bool rowPositiveFlags[N] = { true, true, true };
//Cycle through each row.
for (int row = 0; row < N; ++row)
//Cycle through every column in the current row.
for (int col = 0; col < N; ++col)
//If the column is negative, set the result for this row to false and break
//the column for loop as we don't need to check any more columns for this row.
if (A[row][col] < 0) {
rowPositiveFlags[row] = false;
break;
}
You should always name things so that you can read your code like a book. Your i's, j's, and k's just make something simple confusing. As for the problem, just plan out your solution.
Solve the problem by hand on paper, write the steps in comments in your code, and then write code below the comments so what you do definitely makes sense and isn't overkill.
And this is a great site, but next time, post a smaller piece of code that shows your problem. People shouldn't ever give you a full solution here for homework so don't look for one. Just find the spot where your indices are broken and paste that set of 5 lines or whatever else is wrong. People appreciate that and you'll get faster, better answers for showing the effort :)