Negamax C++ Implemention Give Wrong Result - c++

NOTE: Please comment if you think this post looks to you not having adequate details e.g. codes, results and other stuff; I will edit the post accordingly.
NOTE 2: I wrote this program by hand myself.
I have a negamax implementation the result of which looked very wrong to me i have tried many ways of debugging it but I still can't seem to get the crux of it.
First of all this is a negamax implementation for Tic Tac Toe, which has board of 3X3.
following codes are the full set in order to replicate the error I had wit this algorithm. please comment below if I missed anything.
An Example could be doing this main:
int main {
Board board;
board.startGameNage(0,0);
}
I would expect the game ended in a draw because this is computer (perfect player) vs computer (perfect player), however,Using the following set of functions I got a game ending like below:
current max move is: 0,0, current score is: -inf
current max move is: 0,2, current score is: 3
current max move is: 0,1, current score is: -3
current max move is: 1,1, current score is: 3
current max move is: 2,0, current score is: -3
current max move is: 1,2, current score is: 3
current max move is: 2,1, current score is: -3
current max move is: 1,0, current score is: 3
current max move is: 1,0, current score is: -3
X X O
X O O
X X ---
the " - " there means no move is made in that cell, which looked obviously wrong.
I implemented my minimax first and this negamax was in a way evolving based on my minimax implementation, which might be the reason that I can't see my error.
I got that minimax makes moves from 2 player's perspective and evaluate scores the same as well, whereas negamax make moves from 2 player's perspective but evaluate score only from current player's perspective.
I guess this is the bit that confused me. I can't seem to see how my implementation went wrong here.
I start my game by the following function in main:
// in main I will just give the following function a coordinate, e.g. (0,0)
void Board::startGameNega(const int & row, const int & col){
Move move(row, col);
int player = 1;
for (int depth = 0; depth < 9; depth++){
applyMoveNega(move, player);
Move current_move = move;
move = negaMax(depth, player, move);
player = -player;
cout << "current Max move is: " << current_move.getRow()
<< " , "
<< current_move.getCol()
<< ", Current score is: "
<< current_move.getScore() << endl;
}
print(); // print the end of game board
}
here is the board.hpp:
#define LENGTH 3
#define WIDTH 3
#define CROSS 1
#define NOUGHT -1
#include <iostream>
#include <vector>
#include <array>
#include <map>
#include "Move.hpp"
using namespace std;
#pragma once
typedef vector<Move> Moves;
struct Board {
// constructors;
Board(int width, int length) :m_width(width), m_length(width){};
Board(){};
// destructor;
~Board(){};
// negamax;
Move negaMax(const int & depth, const int & player, const Move & initialMove);
void startGameNega(const int & row, const int & col);
void applyMoveNega(const Move & move, const int & player);
bool isWon(const int & player);
bool isGameComplete();
int evaluateGameStateNega(const int & depth, const int & player);
// share;
int getOpponent(const int & player);
void deleteMove(const Move & move);
void deleteMoves(const Move & initialMove);
// utilities;
static int defaultBoard[WIDTH][LENGTH];
int getWidth() const { return m_width; }
int getLength() const { return m_length; }
void setWidth(int width){ m_width = width; }
void setLength(int length){ m_length = length; }
void print();
int getCurrentPlayer();
private:
int m_width;
int m_length;
enum isWin{ yes, no, draw };
int result;
int m_player;
};
some key elements listed here:
print:
void Board::print(){
for (int i = 0; i < WIDTH; i++) {
for (int j = 0; j < LENGTH; j++) {
switch (defaultBoard[i][j]) {
case CROSS:
cout << "X";
break;
case NOUGHT:
cout << "O";
break;
default:
cout << "-";
break;
}
cout << " ";
}
cout << endl;
}
}
generateMoves:
Moves Board::generateMoves(const int &rowIndex, const int &colIndex){
Moves Moves;
if (defaultBoard){
for (int i = 0; i < WIDTH; i++)
{
for (int j = 0; j < LENGTH; j++)
{
if (i == rowIndex && j == colIndex)
{
continue;
}
else if (defaultBoard[i][j] == 1 || defaultBoard[i][j] == 4)
{
continue;
}
else if (defaultBoard[i][j] == 0)
{
Move move(i, j);
Moves.push_back(move);
}
}
}
}
return Moves;
}
applyMovesNega:
void Board::applyMoveNega(const Move & move, const int & player){
if (player == 1){
defaultBoard[move.getRow()][move.getCol()] = CROSS;
}
else if (player == -1)
{
defaultBoard[move.getRow()][move.getCol()] = NOUGHT;
}
}
isGameComplete:
bool Board::isGameComplete(){
if (defaultBoard[0][0] == defaultBoard[0][1] && defaultBoard[0][1] == defaultBoard[0][2] && defaultBoard[0][0] != 0 ||
defaultBoard[1][0] == defaultBoard[1][1] && defaultBoard[1][1] == defaultBoard[1][2] && defaultBoard[1][0] != 0 ||
defaultBoard[2][0] == defaultBoard[2][1] && defaultBoard[2][1] == defaultBoard[2][2] && defaultBoard[2][0] != 0 ||
defaultBoard[0][0] == defaultBoard[1][0] && defaultBoard[1][0] == defaultBoard[2][0] && defaultBoard[0][0] != 0 ||
defaultBoard[0][1] == defaultBoard[1][1] && defaultBoard[1][1] == defaultBoard[2][1] && defaultBoard[0][1] != 0 ||
defaultBoard[0][2] == defaultBoard[1][2] && defaultBoard[1][2] == defaultBoard[2][2] && defaultBoard[0][2] != 0 ||
defaultBoard[0][0] == defaultBoard[1][1] && defaultBoard[1][1] == defaultBoard[2][2] && defaultBoard[0][0] != 0 ||
defaultBoard[2][0] == defaultBoard[1][1] && defaultBoard[1][1] == defaultBoard[0][2] && defaultBoard[2][0] != 0){
return true;
}
return false;
}
evaluate the score:
int Board::evaluateGameStateNega(const int & depth, const int & player){
int new_score;
isWon(player);
if (result == isWin::yes)
new_score = 10 - depth;
else if (result == isWin::no)
new_score = depth - 10;
else
new_score = 0;
return new_score;
}
deleteMove:
void Board::deleteMove(const Move & move){
defaultBoard[move.getRow()][move.getCol()] = 0;}
here's the move.hpp:
struct Move{
Move(){};
Move(const int & index) :m_rowIndex(index / 3),m_colIndex(index % 3){};
Move(const int & row, const int & col) :m_rowIndex(row), m_colIndex(col){};
Move(const int & row, const int & col, const int & score):m_rowIndex(row), m_colIndex(col), m_score(score){};
~Move(){};
//member functions;
int getRow() const { return m_rowIndex; };
int getCol() const { return m_colIndex; };
void setRow(const int & row){ m_rowIndex = row; };
void setCol(const int & col){ m_colIndex = col; };
void setScore(const int & score){ m_score = score; };
int getScore() const { return m_score; }
private:
int m_rowIndex;
int m_colIndex;
int m_score;
};
This is the actual NegaMax Function:
Move Board::negaMax(const int & depth, const int & curPlayer, const Move & initialMove){
int row = initialMove.getRow();
int col = initialMove.getCol();
int _depth = depth;
int _curplayer = curPlayer;
Moves moves = generateMoves(row, col);
Move bestMove;
Move proposedNextMove;
//change to isGameComplete as of 15/10;
if (_depth == 8 || isGameComplete())
{
int score = evaluateGameStateNega(_depth, _curplayer);
bestMove.setScore(score);
bestMove.setRow(initialMove.getRow());
bestMove.setCol(initialMove.getCol());
}
else{
_depth += 1;
int bestScore = -1000;
for (auto move : moves){
applyMoveNega(move, -_curplayer);
proposedNextMove = negaMax(_depth, -_curplayer, move);
int tScore = -proposedNextMove.getScore();
proposedNextMove.setScore(tScore);
if (proposedNextMove.getScore() > bestScore){
bestScore = proposedNextMove.getScore();
bestMove.setScore(bestScore);
bestMove.setRow(move.getRow());
bestMove.setCol(move.getCol());
}
deleteMove(move);
}
}
return bestMove;
}
I evaluate the game state using following Function:
bool Board::isWon(const int & player){
if (defaultBoard[0][0] == defaultBoard[0][1] && defaultBoard[0][1] == defaultBoard[0][2] && defaultBoard[0][0] == player ||
defaultBoard[1][0] == defaultBoard[1][1] && defaultBoard[1][1] == defaultBoard[1][2] && defaultBoard[1][0] == player ||
defaultBoard[2][0] == defaultBoard[2][1] && defaultBoard[2][1] == defaultBoard[2][2] && defaultBoard[2][0] == player ||
defaultBoard[0][0] == defaultBoard[1][0] && defaultBoard[1][0] == defaultBoard[2][0] && defaultBoard[0][0] == player ||
defaultBoard[0][1] == defaultBoard[1][1] && defaultBoard[1][1] == defaultBoard[2][1] && defaultBoard[0][1] == player ||
defaultBoard[0][2] == defaultBoard[1][2] && defaultBoard[1][2] == defaultBoard[2][2] && defaultBoard[0][2] == player ||
defaultBoard[0][0] == defaultBoard[1][1] && defaultBoard[1][1] == defaultBoard[2][2] && defaultBoard[0][0] == player ||
defaultBoard[2][0] == defaultBoard[1][1] && defaultBoard[1][1] == defaultBoard[0][2] && defaultBoard[2][0] == player){
result = isWin::yes;
return true;
}
else if (defaultBoard[0][0] == defaultBoard[0][1] && defaultBoard[0][1] == defaultBoard[0][2] && defaultBoard[0][0] == -player ||
defaultBoard[1][0] == defaultBoard[1][1] && defaultBoard[1][1] == defaultBoard[1][2] && defaultBoard[1][0] == -player ||
defaultBoard[2][0] == defaultBoard[2][1] && defaultBoard[2][1] == defaultBoard[2][2] && defaultBoard[2][0] == -player ||
defaultBoard[0][0] == defaultBoard[1][0] && defaultBoard[1][0] == defaultBoard[2][0] && defaultBoard[0][0] == -player ||
defaultBoard[0][1] == defaultBoard[1][1] && defaultBoard[1][1] == defaultBoard[2][1] && defaultBoard[0][1] == -player ||
defaultBoard[0][2] == defaultBoard[1][2] && defaultBoard[1][2] == defaultBoard[2][2] && defaultBoard[0][2] == -player ||
defaultBoard[0][0] == defaultBoard[1][1] && defaultBoard[1][1] == defaultBoard[2][2] && defaultBoard[0][0] == -player ||
defaultBoard[2][0] == defaultBoard[1][1] && defaultBoard[1][1] == defaultBoard[0][2] && defaultBoard[2][0] == -player)
{
result = isWin::no;
return true;
}
result = isWin::draw;
return false;
}

thanks #PaulMckenzie for pointing out some of my code issues.
But they were nothing to do with errors I made on core logic of Negamax.
One by one, i will list them all out here and hope it could also help others who want to learn Negamax as well. if I miss anything just comment, I will edit afterwards.
*
DO remember to initialise all your new fields to a value, don't leave
them for the logic to decide what is the initial value. this helps in
debugging and it is just a good code practice. Thanks to #PaulMcKenzie
*
Issue 1: deleteMoveNega() && applyMoveNega()
all they do is deleting a proposed move/applying a proposed move; they don't give back/pass on the turn to current player. Because the move is proposed as the best move for the opponent of current player, once we finish calculating the score for this proposed move (A) and we want to test the next proposed move(B), we will need to delete A and give the turn back to current player. (or, to some people it's better understood as previous player.) the same applies when we apply a proposed move.
it should therefore be:
void Board::deleteMoveNega(const Move & move){
defaultBoard[move.getRow()][move.getCol()] = EMPTY;
m_player = getOpponent(m_player); // give turn back to current player;
}
void Board::applyMoveNega(const Move & move){
defaultBoard[move.getRow()][move.getCol()] = m_player;
m_player = getOpponent(m_player); // pass on the turn to current player;
}
this is the most important error I made because with the old codes I would just propose move and calculate scores as whoever started the game all the way through; because I manually set the player to the opponent in startGameNage(), I then played the game as opponent proposing moves and calculating scores only as the opponent all the way through (whereas I should really be switching context and be in two players' positions). And this happened in each iteration of the negamax function. this didn't enforce the concept of thinking as current player because when I am supposed to play as current player, I however played as opponent.
This is fundamentally wrong in negamax.
Once we fix this, we then don't need to manually set the turn in startGameNage() therefore:
player = -player;
should be deleted and:
int player = 1;
will be changed to:
m_player = 1;
Issue 2: negaMax()
with deleteMove() and applyMove() sorted out, we can now have a look at our negamax engine.
applyMoveNega(move, -_curplayer);
proposedNextMove = negaMax(_depth, -_curplayer, move);
First, I don't need the current player parameter. I have private m_player
which I can make use of.
Second, and more importantly, with old deleteMove() and applyMove() and setting turn manually in startGameNega(), this negation for player here (-_curplayer) is just so wrong.
for example, we apply/make a move for -_curplayer; the proposed move happens next should be for the opponent, which in our case, should be _curplayer. i am still passing -_curplayer, this will then generate moves for the wrong player right from the very beginning.
a new core negamax would be like:
Move Board::negaMax(const int & depth, const Move & initialMove){
int row = initialMove.getRow();
int col = initialMove.getCol();
int _depth = depth;
Move bestMove;
Move proposedNextMove;
//change to isGameComplete as of 15/10;
if (_depth == 8 || isGameComplete())
{
int score = evaluateGameStateNega(_depth);
bestMove.setScore(score);
bestMove.setRow(initialMove.getRow());
bestMove.setCol(initialMove.getCol());
}
else{
Moves moves = generateMoves(row, col);
_depth += 1;
int bestScore = -1000;
for (auto move : moves){
applyMoveNega(move);
proposedNextMove = negaMax(_depth, move);
int tScore = -proposedNextMove.getScore();
proposedNextMove.setScore(tScore);
if (proposedNextMove.getScore() > bestScore){
bestScore = proposedNextMove.getScore();
bestMove.setScore(bestScore);
bestMove.setRow(move.getRow());
bestMove.setCol(move.getCol());
}
deleteMoveNega(move);
}
}
return bestMove;
}
Issue 3: clean up a bit
Yes I just have to admit this piece of algorithm was horribly written only to rush out the logic in my head and only to be prone to errors later. As we progress, we should all be diligent enough to prevent this. However sometimes we still need to get the logic going first :)
I will post clean-ups just to make it work, but not all the clean-ups which are to make it perfect. happy to accept comment.
isWon()
bool Board::isWon(const int & currentPlayer){
if (defaultBoard[0][0] == defaultBoard[0][1] && defaultBoard[0][1] == defaultBoard[0][2] && defaultBoard[0][0] == currentPlayer ||
defaultBoard[1][0] == defaultBoard[1][1] && defaultBoard[1][1] == defaultBoard[1][2] && defaultBoard[1][0] == currentPlayer ||
defaultBoard[2][0] == defaultBoard[2][1] && defaultBoard[2][1] == defaultBoard[2][2] && defaultBoard[2][0] == currentPlayer ||
defaultBoard[0][0] == defaultBoard[1][0] && defaultBoard[1][0] == defaultBoard[2][0] && defaultBoard[0][0] == currentPlayer ||
defaultBoard[0][1] == defaultBoard[1][1] && defaultBoard[1][1] == defaultBoard[2][1] && defaultBoard[0][1] == currentPlayer ||
defaultBoard[0][2] == defaultBoard[1][2] && defaultBoard[1][2] == defaultBoard[2][2] && defaultBoard[0][2] == currentPlayer ||
defaultBoard[0][0] == defaultBoard[1][1] && defaultBoard[1][1] == defaultBoard[2][2] && defaultBoard[0][0] == currentPlayer ||
defaultBoard[2][0] == defaultBoard[1][1] && defaultBoard[1][1] == defaultBoard[0][2] && defaultBoard[2][0] == currentPlayer){
return true;
}
return false;
}
now I realised I didn't have to check for both players; that was wrong, I shall only be checking for current player; and the code is cleaner to read with only one if statement. the result was completely unnecessary. delete them. I was just confusing myself by complicating matters.
evaluateGameStateNega()
following the change in isWon() we would accordingly change the implementation of evaluateGameStateNega() as well:
int Board::evaluateGameStateNega(const int & depth){
if (isWon(m_player))
return 10 - depth;
if (isWon(getOpponent(m_player)))
return depth - 10;
else
return 0;
}
generateMoves()
Above changes suffice to make it work with all other parts being untouched. this one therefore, is to add value.
Moves Board::generateMoves(const int &rowIndex, const int &colIndex){
Moves Moves;
if (defaultBoard){
for (int i = 0; i < WIDTH; i++)
{
for (int j = 0; j < LENGTH; j++)
{
if (defaultBoard[i][j] == 0)
{
Move move(i, j);
Moves.push_back(move);
}
}
}
}
return Moves;
}
obviously I wrote redundant codes. we don't need to check if the cell was occupied or not; we just need to generate moves for all empty cells!

Related

C++ Connect 4 Minimax + Alpha Beta Pruning AI making poor decisions

This post is kind of a follow-up to my last post. To increase the efficiency of a minimax connect-4 AI algorithm, I decided to use alpha-beta pruning. This definitely helped with the long runtime of the program (which I previously believed to be an infinite recursion), but the AI is not working how I want it to.
The AI is simply choosing the next available empty spot to mark, even if it will lead to a loss.
I have tried increasing and decreasing the depth level, and made sure the function that checks for a winner actually works. Furthermore, I converted the 2d vector previously used for the board to a 1d vector, and updated other functions accordingly.
Any help on why the AI is behaving the way it is would be greatly appreciated.
Code:
#include <iostream>
#include <vector>
using namespace std;
bool isFull(std::vector<char>& grid) { //just checks if no empty spaces
for(int i = 0; i < 16; i++) {
if(grid[i] == '-') {
return false;
}
}
return true;
}
pair<bool, char> isWinner(std::vector<char>& grid, char aiMark, char hMark) {
pair<bool, char> temp; // the pair of: whether the game is over, and who won(if any.)
//'X' if AI wins, 'O' if human wins, '-' if tie/game not over.
//horizontal check
for (int i = 0; i < 16; i += 4) {
if (grid[i] == aiMark && grid[i + 1] == aiMark &&
grid[i + 2] == aiMark && grid[i + 3] == aiMark) {
temp.first = true;
temp.second = aiMark;
return temp;
}
else if (grid[i] == hMark && grid[i + 1] == hMark &&
grid[i + 2] == hMark && grid[i + 3] == hMark) {
temp.first = true;
temp.second = hMark;
return temp;
}
}
//vertical check
for (int i = 0; i < 4; i++) {
if (grid[i] == aiMark && grid[i + 4] == aiMark &&
grid[i + 8] == aiMark && grid[i + 12] == aiMark) {
temp.first = true;
temp.second = aiMark;
return temp;
}
else if (grid[i] == hMark && grid[i + 4] == hMark &&
grid[i + 8] == hMark && grid[i + 12] == hMark) {
temp.first = true;
temp.second = hMark;
return temp;
}
}
//diagonal checks
if (grid[0] == aiMark && grid[5] == aiMark &&
grid[10] == aiMark && grid[15] == aiMark) {
temp.first = true;
temp.second = aiMark;
return temp;
}
else if (grid[0] == hMark && grid[5] == hMark &&
grid[10] == hMark && grid[15] == hMark) {
temp.first = true;
temp.second = hMark;
return temp;
}
if (grid[3] == aiMark && grid[6] == aiMark &&
grid[9] == aiMark && grid[12] == aiMark) {
temp.first = true;
temp.second = aiMark;
return temp;
}
else if (grid[3] == hMark && grid[6] == hMark &&
grid[9] == hMark && grid[12] == hMark) {
temp.first = true;
temp.second = hMark;
return temp;
}
if (isFull(grid) == true) {
temp.first = true;
temp.second = '-';
return temp;
}
temp.first = false;
temp.second = '-';
return temp;
}
int minimax(std::vector<char>& grid, int depth, bool maxim,
char aiMark, char hMark, int al, int be) {
pair<bool, char> result = isWinner(grid, aiMark, hMark);
// result.first will be true if game is over, and result.second is:
// 'X' if ai wins, 'O' if human wins, '-' if game is not over or if it ends with tie
if (result.first != false || depth == 0) {
if (result.second == aiMark) {
return depth; // AI wins (maximizing)
}
else if (result.second == hMark) {
return -depth; // Human wins (minimizing)
}
else {
return 0; // Tie or depth = 0
}
}
else {
if (maxim == true) {
int best = INT_MIN;
for (int i = 0; i < 16; i++) {
if (grid[i] == '-') { // is space empty?
grid[i] = aiMark; // editing board
int score = minimax(grid, depth - 1, !maxim, aiMark, hMark, al, be); // call minimax with "new" board
best = max(best, score); // update max
grid[i] = '-'; // backtrack
al = best; // update alpha
if (al >= be) {
break; // pruning
}
}
}
return best; //return max score
}
else {
int worst = INT_MAX;
for (int i = 0; i < 16; i++) {
if (grid[i] == '-') {
grid[i] = hMark;
int score = minimax(grid, depth - 1, !maxim, aiMark, hMark, al, be);
worst = min(worst, score);
grid[i] = '-';
be = worst;
if (be <= al) { //same as the maximizing player but is minimizing instead
break;
}
}
}
return worst; //return min score
}
}
}
void bestMove(std::vector<char>& grid, char aiMark, char hMark) {
int best = INT_MIN; //best score for ai
int finalSpot = -1; //place where ai will put mark
pair<bool, char> result = isWinner(grid, aiMark, hMark); // explained in minimax function comments
if (result.first != false) {
return; // if game is supposed to be over
}
for (int i = 0; i < 16; i++) {
if (grid[i] == '-') {
grid[i] = aiMark;
int score = minimax(grid, 8, true, aiMark, hMark, INT_MIN, INT_MAX);
if (score > best) {
best = score;
finalSpot = i; // update best score and best spot
}
grid[i] = '-'; // backtrack
}
}
grid[finalSpot] = aiMark; // AI finally updates grid
return;
}
The algorithm can choose a losing move, because in bestMove(), you place an aiMark, then call minmax() with maxim set to true which will place a second aiMark in a row. The human does not play after the IA does.
Concerning alpha beta, you can also update alpha with : alpha = max(alpha, best), and the equivalent way with beta. The way you did is not wrong but not optimized, as the value of alpha can drop while it should only raise.
I think the best way to solve your game is to add a transposition table. It is a bit heavy to implement but IA will avoid studying twice the same position. You can first transform your code to a Negamax version which is easy and convenient.

My 2D maze solver recurses infinitely and I get a stack overflow - why?

The Problem :-
I am trying to solve a 2d maze navigation problem in C++ using 2-dimensional array. To give a concise idea about the problem itself, I intend to navigate from node 'S' in the array to node 'G' by walking through free spaces denoted by '.' The nodes '#' are obstacles. One is not allowed to move on spaces denoted as obstacles. Care must also be taken to make all moves as legal moves (within configuration space). I denote the valid move with a '+' after replacement of the '.' If you like to know more about this problem (not necessary) then please refer this link.
What is the issue ?
I coded a recursive algorithm for this problem where we receive an array and a start node position, and then try to navigate to the goal node using recursion. However, I am getting a stack overflow error. It seems like my recursion never stops. I strongly believe there is some problem in my play() function or my check() function. I am not sure what actually is the problem.
What did I try ?
I am reproducing my code below :
void spawn(std::string (&board)[6]) {
for (int i = 0; i <= 6; i++) {
std::cout << board[i] << std::endl;
}
}
bool check(size_t a, size_t b, const std::string (&board)[6]) {
if (a < board[1].size() && a >= 0 && b < board[1].size() && b >= 0) {
if (board[a][b] == '#' || board[a][b] == '+')
return false;
else if (board[a][b] == '.')
return true;
}
return false;
}
void play(std::string (&board)[6], size_t a, size_t b) {
auto status = check(a, b, board);
if (board[a][b] == 'G' || board[a][b] == 'g') {
spawn(board);
return;
}
if (status) {
board[a][b] = '+';
play(board, ++a, b);
play(board, --a, b);
play(board, a, ++b);
play(board, a, --b);
}
}
int main() {
std::string grid[6] = {{"S#####"},
{".....#"},
{"#.####"},
{"#.####"},
{"...#.G"},
{"##...#"}};
play(grid, 0, 0);
return 0;
}
The check function prevents recursion because it sees the 'S' in the grid at the starting location. Changing:
else if (board[a][b] == '.')
to
else if (board[a][b] == '.' || board[a][b] == 'S')
got it to work for me.
Thanks for the insights Perette and Retired Ninja. I refactored the play() and check() functions in light of your suggestions and also some more ideas by myself.
I figured out that the main issue with the segmentation fault error was not providing accommodation for the '\0' character at the end of array of strings grid. I overlooked it because I considered array of strings to function in a different way than array of chars (since they are not the same species). Now I realize that the '\0' is necessary even for an array of strings !
I am reproducing the refactored functions for the purpose of completeness of this post :
void spawn(std::string board[6]) {
for (int i = 0; i <= 6; i++) {
std::cout << board[i] << std::endl;
}
}
bool check(int a, int b, const std::string board[6]) {
if (a < board[1].size() && a >= 0 && b <
board[1].size() && b >= 0) {
if (board[a][b] == '#' || board[a][b] == '+') {
return false;
} else if (board[a][b] == '.' ||
board[a][b] == 'S' ||
board[a][b] == 'G') {
return true;
}
}
return false;
}
void play(std::string board[6], int a, int b) {
if (board[a][b] == 'G' || board[a][b] == 'g') {
board[0][0] = 'S';
spawn(board);
return;
}
if (board[a][b] == '.' || board[a][b] == 'S')
board[a][b] = '+';
if (check(a + 1, b, board)) play(board, a + 1, b);
if (check(a - 1, b, board)) play(board, a - 1, b);
if (check(a, b + 1, board)) play(board, a, b + 1);
if (check(a, b - 1, board)) play(board, a, b - 1);
if (board[a][b] == '+') board[a][b] = '.';
}
int main() {
std::string grid[7] = {{"S#####"},
{".....#"},
{"#.####"},
{"#.####"},
{"...#.G"},
{"##...#"}};
play(grid, 0, 0);
return 0;
}

spoj samer08e getting wrong answer

Here is my solution for the spoj problem:
http://www.spoj.com/problems/SAMER08E/
This is my logic:
I check if the given dates are adjacent to each other. If they are, then add the difference of the cost. Else skip.
//header files omitted
#define REP(i,n) for(int i=0; i<n; i++)
#define FOR(i,st,end) for(int i=st;i<end;i++)
int monthDates[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
bool isLeap( int y ){//check if year is leap
if(y%4 == 0 && y%100 != 0 || y%400 == 0)
return true;
return false;
}
bool checkAdjacent( int prev[], int curr[] ){//check if the dates are adjacent to each other
if( prev[0] == 31 && prev[1] == 12 && curr[0] == 1 && curr[1] == 1 && (curr[2] - prev[2]) == 1 )//last and first days of the year
return true;
if( prev[2] == curr[2]){//same year
if( prev[1] == curr[1]){//same month
if((curr[0]-prev[0]) == 1)//same adjacent dates
return true;
}else if( curr[1] - prev[1] == 1){//adjacent months
if( isLeap(prev[2]) && prev[1] == 2 && curr[1] == 3 && prev[0] == 29 && curr[0] == 1)//for leap year february
return true;
else if( !isLeap(prev[2]) && monthDates[prev[1]] == prev[0] && curr[0] == 1)
return true;
}
}
return false;
}
int main(){
int n;
while( scanf("%d", &n) && n){
int prev[4], curr[4], count = 0;
ll totalCost = 0;
REP(i,4)
scanf("%d", &prev[i]);
FOR( i, 1, n){
REP(j,4)
scanf("%d", &curr[j]);
if( checkAdjacent( prev, curr) ){
totalCost += curr[3] - prev[3];
count++;
}
prev[0] = curr[0];
prev[1] = curr[1];
prev[2] = curr[2];
prev[3] = curr[3];
}
printf("%d %lld\n", count, totalCost);
}
return 0;
}
The program works for the test cases, but i keep getting wrong answer. What is the error?
Since there are two people recording the measurements, it might be possible for the same day to be recorded twice, which could throw off your consumption computation.
Your program does not check the condition when the year is leap and the month is not February. Add this condition and you will get AC. I hope this helps.

Sudoku solving algorithm C++

I'm trying to make a Sudoku Solving program for a couple of days but I'm stuck with the methods. I found this algorithm here but I don't really understand it:
start at the first empty cell, and put 1 in it.
Check the entire board, and see if there are any conflicts
If there are coflicts on the board, increase the number in the current cell by 1 (so change 1 to 2, 2 to 3, etc)
If the board is clean move, start at step one again.
If all nine possible numbers on a given cell cause a conflict in the board, then you set this cell back to empty, go back to the previous cell, and start again from step 3 (this is where the 'backtracking' comes in).
Here is my code. I think something is wrong with my Help_Solve(...) function. Can you help me to identify the problem, please?
#include <iostream>
#include <iomanip>
#include <time.h>
#include <cstdlib>
#include <windows.h>
using namespace std;
class Sudoku
{
private:
int board[9][9];
int change[9][9];
public:
Sudoku();
void Print_Board();
void Add_First_Cord();
void Solve();
void Help_Solve(int i, int j);
bool Check_Conflicts(int p, int i, int j);
};
Sudoku Game;
void setcolor(unsigned short color) //The function that you'll use to
{ //set the colour
HANDLE hcon = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hcon,color);
}
Sudoku::Sudoku()
{
for(int i = 1; i <= 9; i++)
for(int j = 1; j <= 9; j++)
board[i][j] = 0;
}
void Sudoku::Print_Board()
{
for(int i = 1; i <= 9; i++)
{
for(int j = 1; j <= 9; j++)
{
if(change[i][j] == 1)
{
setcolor(12);
cout << board[i][j] << " ";
setcolor(7);
}
else cout << board[i][j] << " ";
if(j%3 == 0) cout << "| ";
}
cout << endl;
if(i%3 == 0) cout << "------+-------+---------" << endl;
}
}
void Sudoku::Add_First_Cord()
{
board[1][1] = 5; change[1][1] = 1;
board[1][2] = 3; change[1][2] = 1;
board[1][5] = 7; change[1][5] = 1;
board[2][1] = 6; change[2][1] = 1;
board[2][4] = 1; change[2][4] = 1;
board[2][5] = 9; change[2][5] = 1;
board[2][6] = 5; change[2][6] = 1;
board[3][2] = 9; change[3][2] = 1;
board[3][3] = 8; change[3][3] = 1;
board[3][8] = 6; change[3][8] = 1;
board[4][1] = 8; change[4][1] = 1;
board[4][5] = 6; change[4][5] = 1;
board[4][9] = 3; change[4][9] = 1;
board[5][1] = 4; change[5][1] = 1;
board[5][4] = 8; change[5][4] = 1;
board[5][6] = 3; change[5][6] = 1;
board[5][9] = 1; change[5][9] = 1;
board[6][1] = 7; change[6][1] = 1;
board[6][5] = 2; change[6][5] = 1;
board[6][9] = 6; change[6][9] = 1;
board[7][2] = 6; change[7][2] = 1;
board[7][7] = 2; change[7][7] = 1;
board[7][8] = 8; change[7][8] = 1;
board[8][4] = 4; change[8][4] = 1;
board[8][5] = 1; change[8][5] = 1;
board[8][6] = 9; change[8][6] = 1;
board[8][9] = 5; change[8][9] = 1;
board[9][5] = 8; change[9][5] = 1;
board[9][8] = 7; change[9][8] = 1;
board[9][9] = 9; change[9][9] = 1;
}
bool Sudoku::Check_Conflicts(int p, int i, int j)
{
for(int k = 1; k <= 9; k++)
if(board[i][k] == p) return false;
for(int q = 1; q <= 9; q++)
if(board[q][j] == p) return false;
/*
*00
000
000
*/
if((j == 1 || j == 4 || j == 7) && (i == 1 || i == 4 || i == 7))
{
if(board[i][j+1] == p || board[i][j+2] == p || board[i+1][j] == p ||
board[i+2][j] == p || board[i+1][j+1] == p || board[i+1][j+2] == p ||
board[i+2][j+1] == p || board[i+2][j+2] == p)return false;
}
/*
000
000
*00
*/
if((j == 1 || j == 4 || j == 7) && (i == 3 || i == 6 || i == 9))
{
if(board[i-1][j] == p || board[i-2][j] == p || board[i][j+1] == p ||
board[i][j+2] == p || board[i-1][j+1] == p || board[i-1][j+2] == p ||
board[i-2][j+1] == p || board[i-2][j+2] == p)return false;
}
/*
000
*00
000
*/
if((j == 1 || j == 4 || j == 7) && (i == 2 || i == 5 || i == 8))
{
if(board[i-1][j] == p || board[i+1][j] == p || board[i-1][j+1] == p ||
board[i][j+1] == p || board[i+1][j+1] == p || board[i+1][j+2] == p ||
board[i][j+2] == p || board[i+1][j+2] == p)return false;
}
/*
0*0
000
000
*/
if((j == 2 || j == 5 || j == 8) && (i == 1 || i == 5 || i == 7))
{
if(board[i-1][j] == p || board[i+1][j] == p || board[i-1][j+1] == p ||
board[i][j+1] == p || board[i+1][j+1] == p || board[i+1][j+2] == p ||
board[i][j+2] == p || board[i+1][j+2] == p)return false;
}
/*
000
0*0
000
*/
if((j == 2 || j == 5 || j == 8) && (i == 2 || i == 5 || i == 8))
{
if(board[i-1][j] == p || board[i-1][j-1] == p || board[i-1][j+1] == p ||
board[i][j+1] == p || board[i][j-1] == p || board[i+1][j+1] == p ||
board[i][j] == p || board[i+1][j-1] == p)return false;
}
/*
000
000
0*0
*/
if((j == 2 || j == 5 || j == 8) && (i == 3 || i == 6 || i == 9))
{
if(board[i][j-1] == p || board[i][j+1] == p || board[i-1][j] == p ||
board[i-1][j+1] == p || board[i-1][j-1] == p || board[i-2][j] == p ||
board[i-1][j+1] == p || board[i-2][j-1] == p) return false;
}
/*
00*
000
000
*/
if((j == 3 || j == 6 || j == 9) && (i == 1 || i == 4 || i == 7))
{
if(board[i][j-1] == p || board[i][j-2] == p || board[i+1][j] == p ||
board[i+1][j-1] == p || board[i+1][j-2] == p || board[i+2][j] == p ||
board[i+2][j-1] == p || board[i+2][j-2] == p) return false;
}
/*
000
00*
000
*/
if((j == 3 || j == 6 || j == 9) && (i == 2 || i == 5 || i == 8))
{
if(board[i-1][j] == p || board[i-1][j-1] == p || board[i-1][j-2] == p ||
board[i][j-1] == p || board[i][j-2] == p || board[i+1][j] == p ||
board[i+1][j-1] == p || board[i+1][j-2] == p) return false;
}
/*
000
000
00*
*/
if((j == 3 || j == 6 || j == 9) && (i == 3 || i == 6 || i == 9))
{
if(board[i][j-1] == p || board[i][j-1] == p || board[i-1][j] == p ||
board[i-1][j-1] == p || board[i-1][j-2] == p || board[i-2][j] == p ||
board[i-2][j-1] == p || board[i-2][j-2] == p) return false;
}
return true;
}
void Sudoku::Help_Solve(int i, int j)
{
if(j <= 0)
{
i = i-1;
j = 9;
}
if(change[i][j] == 1) return Game.Help_Solve(i, j-1);
for(int p = 1; p <= 9; p++)
if(Game.Check_Conflicts(p, i, j))
{
board[i][j] = p;
return;
}
return Game.Help_Solve(i, j-1);
}
void Sudoku::Solve()
{
for(int i = 1; i <= 9; i++)
{
for(int j = 1; j <= 9; j++)
{
if(board[i][j] == 0 && change[i][j] == 0)
{
Game.Help_Solve(i, j);
}
}
}
for(int i = 1; i <= 9; i++)
for(int j = 1; j <= 9; j++)
if(board[i][j] == 0) Game.Help_Solve(i, j);
}
int main()
{
Game.Add_First_Cord();
Game.Solve();
Game.Print_Board();
system("pause");
return 0;
}
Edit: I need to use recursion right? But maybe the parameters I give to the function are wrong. I really don't know. In Add_First_Cord() I declare the starting values that every sudoku has in the beginning. Here are the values that I use: http://bg.wikipedia.org/wiki/%D0%A4%D0%B0%D0%B9%D0%BB:Sudoku-by-L2G-20050714.gif. I expect to see the solved sudoku as it is shown in wikipedia. But some solved values are right others are not. Here is what I get in the console
Suggested Approach
Implement a generic graph search algorithm
could use either IDFS or A* graph search
I would prefer the second
do this for a general directed graph
node type TNode
node successor function TNode => vector<TNode>
Define your Sudoku states
a state is a 9x9 array with a number 1, 2, ..., or 9 or a blank in each position
Define what a goal Sudoku state is
all 81 cells filled in
all 9 rows have numbers {1, 2, ..., 9} in them
all 9 columns have numbers {1, 2, ..., 9} in them
all 9 3x3 squares have numbers {1, 2, ..., 9} in them
Define your valid Sudoku state successor function
a state S can have number N added at row I, column J if:
cell (I,J) is empty
there is no other N in row I
there is no other N in column J
there is no other N in the 3x3 square containing (I,J)
the state successor function maps a state S to the vector of states that satisfy these rules
Apply your generic graph search algorithm (1) to the Sudoku state graph (2-4)
(optional) If you do choose to use A* graph search, you can also define a heuristic on your Sudoku state space to potentially drastically increase performance
how to design the heuristic is another whole problem, that's more of an art than a science
Current Approach
Your current approach mixes the specification of the graph to be searched and the implementation of the search algorithm. You're going to have a lot of difficulty if you mix those two. This problem naturally separates into two distinct pieces -- the algorithm and the graph -- so you can and should exploit that in your implementation. It will make it much simpler.
The other benefit you get if you go with this separation is that you will be able to reuse your graph search algorithm on a huge number of problems - very cool!
The following assumes you are trying to solve a given board, not generate a puzzle.
Basic (simple) approach
Create a class whose objects can hold a board (here called board_t). This class may internally use array, but must support copying boards.
Have a function void solve(board_t const& board); which repeats the following for each number n:
Copies your input
Enters n in the first empty cell of the copied board
If the copied board is a solution, print the solution and return.
Else If the board is still viable (e.g. no conflicts):
call solve(copied_board)
Performance
This is a recursive backtracking solution, which performs horribly for hard problems. You can significantly speed it up by proper pruning or deductive steps (e.g. if you end up with 8 numbers in a row after inserting one, you can immediately enter the ninth without any kind of search).
Reasoning
While certainly not an impressive technique, it has a high probability of working correctly, since you will only ever be modifying a copy to add a single value. This prevents corruption of your data structures (one problem your idea has is that it will destroy the numbers it finds when backtracking, are not necessarily the ones you just inserted, but may be part of the initial puzzle).
Improving performance is quite simple, once you start picking more intelligent heuristics (e.g. instead of testing the square in order, you could pick the ones with the fewest remaining moves and try to get them out of the way - or do the reverse...) or start doing a bit of deduction and pruning.
Note: The Algorithm Design Manual uses a Soduko solver to show the impact of these techniques on backtracking.
There is one very important modification to recursive algorithms: Use most constrained first approach. This means first to solve a cell with smallest number of possible candidates (when direct row/column/block conflicts are removed).
Another modification is: Change the board in-place; do not copy it. In each recursive call you modify only one cell on the board, and that cell used to be empty. If that call doesn't end up in a solved board somewhere down the recursive call tree, just clear the cell again before returning - this returns the board into original state.
You can find a very short and fast solution in C# on address: Sudoku Solver. It solves arbitrary sudoku board in about 100 steps only, all thanks to the most constrained first heuristic.
This is a classic Constraint Satisfaction Problem. I recommend doing some research on the topic to figure out the successful strategy. You will need to use AC-3 ( Arc Consistency 3) algorithm along with the backtracking techniques to solve the problem.

Is there a way to move two squares in OpenGL simultaneously?

so I have a function that handles key presses in a game I'm working on in OpenGL. But, the thing is that even though I have made two squares and they both move when the correct key is pressed only one square is moved. Is there a way I can make the two squares move. This is the glutKeyboardFunc function I implimented:
void handleKeypress(unsigned char key, int x, int y)
{
if (key == 'w')
{
for (int i = 0; i < 12; i++)
{
if (i == 1 || i == 7 || i == 10 || i == 4)
{
square[i] = square[i] + 0.1;
}
}
}
if (key == 'd')
{
for (int i = 0; i < 12; i++)
{
if (i == 0 || i % 3 == 0)
{
square[i] = square[i] + 0.1;
}
}
}
if (key == 's')
{
for (int i = 0; i < 12; i++)
{
if (i == 1 || i == 7 || i == 10 || i == 4)
{
square[i] = square[i] - 0.1;
}
}
}
if (key == 'a')
{
for (int i = 0; i < 12; i++)
{
if (i == 0 || i % 3 == 0)
{
square[i] = square[i] - 0.1;
}
}
}
glutPostRedisplay();
}
If you need any more code just ask.
Edited for comments below.
// I'm making some assumptions about your functions here. Make adjustments.
// You can handle both players' key inputs here.
void handleKeypress(unsigned char key, int x, int y)
{
if (key == 27)
exit(0);
// Player 1
if (key == 'w')
{
A.moveSquareUp();
}
if (key == 'd')
{
A.moveSquareRight();
}
if (key == 's')
{
A.moveSquareDown();
}
if (key == 'a')
{
A.moveSquareLeft();
}
}
void handleSpecialKeypress(int key, int x, int y)
{
// Player 2
if (key == GLUT_KEY_UP)
{
B.moveSquareUp();
}
if (key == GLUT_KEY_RIGHT)
{
B.moveSquareRight();
}
if (key == GLUT_KEY_DOWN)
{
B.moveSquareDown();
}
if (key == GLUT_KEY_LEFT)
{
B.moveSquareLeft();
}
}
You need to handle your keyboard events somewhere in your game logic (the main loop, or a callback from glutKeyboardFunc()), and call the desired behaviours. This has some advantages:
Your keyboard input handling is unified in one place.
Using if instead of switch allows multiple keys being used.
You can organise this event handling inside your main loop, instead of depending on the timer.