Hi everyone,
I am trying to implement Minimax with alpha-beta pruning method into my game "Connect Four" and also add scores to players and i have a hard time to finish this project. Basically i need to implement AI where computer tries to get as many points as possible. Prompt will allow you to enter characters from 'a' to 'g', which indicate the column and also you can enter '?' which gives you a hint where to put your 'O'(it is also considered AI because computer will tell you that). After your (human) turn, the machine will inspect all the possible turns by placing 'x' move to the board and select the one with the highest score. The board is reset. In the second round, the machine applies the human turn by placing 'o' move to the board in the same way and selects the best turn. This means that the machine is allowing you (human) to play in two consecutive rounds (double play). In this way, the machine can find out a devastating turn missed from its own computation of turns, in which case by preoccupying that endangered position by placing 'x' (thus not allowing to place 'o') the machine can avoid immediate lose and continue the game. This is not a deep-thinker player, and therefore the machine may easily lose. As far as scores i will give you a sample of output
a b c d e f g
| . | . | . | . | . | . | . |
| . | . | . | . | . | . | . |
| . | . | . | . | . | . | . |
| . | . | . | x | . | . | . |
| . | . | . | x | . | o | . |
| . | x | . | o | o | x | o |
Enter your turn: ?
If you chose (a) - H (0, 0), V (0, 3), L (0, 0), R (0, 3), total score 8
If you chose (b) - H (0, 2), V (0, 3), L (0, 2), R (0, 4), total score 13
If you chose (c) - H (2, 0), V (0, 3), L (0, 2), R (0, 0), total score 11
If you chose (d) - H (0, 6), V (0, 2), L (2, 3), R (0, 5), total score 72
If you chose (e) - H (1, 1), V (1, 3), L (0, 0), R (1, 2), total score 14
If you chose (f) - H (0, 2), V (1, 3), L (0, 4), R (1, 2), total score 17
If you chose (g) - H (1, 1), V (1, 3), L (0, 3), R (0, 0), total score 13
As you can see H is horizontal with the first value - player's mark and the second value - empty spaces. To calculate scores you add all spaces+2*player's marks. This is the score for all H, V, L, R. Example:
Hint will tell you where you can place your circle, if you chose (a)
(1+0)*2+(3+3)=8 where 1 is your chosen 'o'. If you chose (c) (2+1)*2+5(spaces)=11. If you got 3 'o's in H or V or R or L you will get extra 50 points. You will get 100 points if you get all 4 'o'. If you have any questions feel free to ask. Code is below what i did so far.
PS: no need to help me with the other Horizontal, Right and Left code I did it.
Just with scoring. Thanks
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <vector>
#include <algorithm>
#include <cassert>
using namespace std;
struct MyGameDef { // professional way to define global enumerable values.
enum Player { Human, Progm, Empty };
Player m_player;
MyGameDef (const Player p) : m_player(p) {}
operator Player() const { return m_player; }
};
class Cell { // representing a cell of a board.
MyGameDef::Player m_occu; // marked 'Empty', 'Human', and 'Progm'.
public:
Cell() : m_occu(MyGameDef::Empty) {}
void setTurn(const MyGameDef::Player p) { assert(!isOccupied()); m_occu = p; }
bool isOccupied() const { return m_occu != MyGameDef::Empty; }
bool isMe(const MyGameDef::Player p) const { return m_occu == p; }
void erase() { m_occu = MyGameDef::Empty; }
friend ostream& operator<<(ostream& o, const Cell& c) {
char mark;
switch(c.m_occu) {
case MyGameDef::Empty: return o << " . ";
case MyGameDef::Human: return o << " o ";
case MyGameDef::Progm: return o << " x ";
default: abort();
}
}
};
class Eval {
int m_conn; // connected to me.
int m_poss; // possible spaces.
public:
Eval(const int c = 0, const int p = 0) : m_conn(c), m_poss(p) {}
bool isConnect4() const { return m_conn >= 3; }
bool isWinning() const { return m_conn == 2 && m_poss >= 1; }
int getValue() const { return m_conn * 2 + m_poss; }
friend ostream& operator<<(ostream& o, const Eval& e) {
return o << "(" << e.m_conn << ", " << e.m_poss << ")";
}
};
class Turn {
char m_turn;
int m_score;
public:
Turn(const char c = '\0', const int s = 0) : m_turn(c), m_score(s) {}
char getTurn() const { return m_turn; }
int getScore() const { return m_score; }
friend bool operator<(const Turn& lhs, const Turn& rhs) {
return lhs.m_score < rhs.m_score;
}
friend ostream& operator<<(ostream& o, const Turn& t) {
return o << "\t--> (" << t.m_turn << ") with score " << t.m_score;
}
};
class Board { // represents Connect Four board.
int m_row;
int m_col;
const int m_max; // used to detect tie.
int m_num; // the number of the marks placed.
vector<vector<Cell> > m_map;
Eval horizontal(const int r, const int c, const MyGameDef::Player p) const {
int count = 0, possible = 0;
bool connected = true;
for (int col = c + 1, saw = 0; col < m_col && saw < 3; ++col, ++saw) { // east direction.
if (m_map[r][col].isMe(p)) {
if (++count > 2) { if (connected) break; }
continue;
}
connected = false;
if (m_map[r][col].isOccupied()) break;
++possible;
}
for (int col = c - 1, saw = 0; col >= 0 && saw < 3; --col, ++saw) { // west direction.
if (m_map[r][col].isMe(p)) {
if (++count > 2) { if (connected); break; }
continue;
}
connected = false;
if (m_map[r][col].isOccupied()) break;
++possible;
}
return Eval(count, possible);
}
public:
Board(const int r = 6, const int c = 7) :
m_row(r), m_col(c), m_max(r * c), m_num(0) {
vector<Cell> arow(m_col, Cell());
for (int i = 0; i < m_row; ++i) m_map.push_back(arow);
}
bool isTie() const { return m_num >= m_max; }
bool isValid(const char c, const MyGameDef::Player p) {
int col = c - 'a';
if (col < 0 || col >= m_col) return false;
bool placed = false;
for (int row = m_row - 1; row >= 0; --row) {
if (m_map[row][col].isOccupied()) continue;
placed = true;
++m_num;
m_map[row][col].setTurn(p); break;
}
return placed;
}
bool isWon(const char c, const MyGameDef::Player p) const {
int col = c - 'a', row = 0;
for (; row < m_row; ++row) {
if (!m_map[row][col].isOccupied()) continue;
assert(m_map[row][col].isMe(p));
break;
}
Eval V = vertical(row, col, p); // check vertical line.
Eval H = horizontal(row, col, p); // check horizontal line.
Eval L = leftup(row, col, p); // check leftup diagnal line.
Eval R = rightup(row, col, p); // check rightup diagnal line.
return V.isConnect4() || H.isConnect4() || L.isConnect4() || R.isConnect4();
}
char findBestMove(const MyGameDef::Player p, const bool show = false) {
cout << "findBestMove function not implemented." << endl;
return char('a' + rand() % 7); // just showing the machine move.
}
void erase(const char c, const MyGameDef::Player p) {
int col = c - 'a';
for (int row = 0; row < m_row; ++row) {
if (!m_map[row][col].isOccupied()) continue;
assert(m_map[row][col].isMe(p));
m_map[row][col].erase(); break;
}
--m_num;
}
friend ostream& operator<<(ostream& o, const Board& m) {
o << endl;
for (int col = 0; col < m.m_col; ++col)
o << setw(3) << (char)('a' + col) << ' ';
o << endl;
for (int row = 0; row < m.m_row; ++row) {
o << '|';
for (int col = 0; col < m.m_col; ++col)
o << setw(3) << m.m_map[row][col] << '|';
o << endl;
}
o << endl;
return o;
}
};
|
if u need main i will post it