-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathconnectfour.h
More file actions
286 lines (250 loc) · 6.8 KB
/
connectfour.h
File metadata and controls
286 lines (250 loc) · 6.8 KB
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
#ifndef CONNECTFOUR_H_
#define CONNECTFOUR_H_
namespace con4 {
//defined as const variables instead of #define
// to avoid problems with multiple declarations
//constants here
const int VACANT = 0;
const int RED = 1;
const int BLACK = -1;
//board dimensions
const int ROW = 6;
const int COL = 7;
//helper functions, inline these if necessary
bool isRed(int x) {
return x == RED;
}
bool isBlack(int x) {
return x == BLACK;
}
bool isVacant(int x) {
return x == VACANT;
}
//return if both a and b are on same/opposite side, respectively
bool isMatching(int a, int b) {
return isRed(a) ? isRed(b) : (isBlack(a) ? isBlack(b) : false);
}
bool isOpposite(int a, int b) {
return isRed(a) ? isBlack(b) : (isBlack(a) ? isRed(b) : false);
}
} //namespace con4
#include <cstddef>
#include <utility>
namespace con4 {
//object representation of board position
//only allows 1 board per run due to static int **board
struct BoardPos {
//pointer to board as a 2d int array
static int **board;
//position represented by two ints
//change to unsigned?
int r, c;
//constructors
BoardPos()
: r(), c() {
}
BoardPos(int r, int c)
: r(r), c(c) {
}
//copy constructor and equals overload
BoardPos(const BoardPos& p)
: r(p.r), c(p.c) {
}
BoardPos& operator=(const BoardPos& p) {
r = p.r, c = p.c;
return *this;
}
//return value of piece on board
//because *BoardPos(1,2) looks nicer than board[1][2]?
//lol get outta here
int& operator*() {
return board[r][c];
}
int& operator*() const {
return board[r][c];
}
//position arithmetic
//treats position as a vector
BoardPos operator-() const {
return BoardPos(-r, -c);
}
BoardPos operator+(const BoardPos& p) const {
return BoardPos(r + p.r, c + p.c);
}
BoardPos& operator+=(const BoardPos& p) {
return *this = (*this + p);
}
BoardPos operator-(const BoardPos& p) const {
return *this + -BoardPos(p);
}
BoardPos& operator-=(const BoardPos& p) {
return *this = (*this - p);
}
//midpoint of this and p
BoardPos operator/(const BoardPos& p) const {
return BoardPos((r + p.r) / 2, (c + p.c) / 2);
}
bool operator==(const BoardPos& p) const {
return r == p.r && c == p.c;
}
};
//just to be pedantic
int **BoardPos::board = NULL;
typedef std::pair<BoardPos, BoardPos> ifpos_pair_type;
} //namespace con4
#include <iostream>
#include <limits>
#include <iomanip>
namespace con4 {
//return char representation of how pieces should be displayed
char pieceRep(int i) {
switch(i) {
case RED:
return 'r';
case BLACK:
return 'b';
}
return ' ';
}
//output board representation to cout
void printBoard() {
std::cout << "+---+---+---+---+---+---+---+\n";
for(int l = 0; l < ROW; ++l) {
for(int m = 0; m < COL; ++m)
std::cout << "|" << std::setw(2) << pieceRep(*BoardPos(l, m)) << " ";
std::cout << "|\n+---+---+---+---+---+---+---+\n";
}
std::cout << " 1 2 3 4 5 6 7\n";
}
//the board with int values being manipulated as the game progresses
int **board = NULL;
//the default board
const int INITBOARD[ROW][COL] = { { VACANT, VACANT, VACANT, VACANT, VACANT, VACANT, VACANT }, { VACANT, VACANT, VACANT, VACANT, VACANT, VACANT, VACANT }, { VACANT, VACANT, VACANT, VACANT, VACANT, VACANT, VACANT }, { VACANT, VACANT, VACANT, VACANT, VACANT, VACANT, VACANT }, { VACANT, VACANT, VACANT, VACANT, VACANT, VACANT, VACANT }, { VACANT, VACANT, VACANT, VACANT, VACANT, VACANT, VACANT } };
//flag for whether it's black's turn
//isBlackTurn = !isBlackTurn to flip turns
bool isBlackTurn = false;
//delete old board and make new board
void initBoard() {
//delete old board
if(board != NULL) {
for(int i = 0; i < ROW; ++i)
delete board[i];
delete board;
}
//and...
//make new board
board = new int*[ROW];
for(int i = 0; i < ROW; ++i) {
board[i] = new int[COL];
for(int j = 0; j < COL; ++j)
board[i][j] = INITBOARD[i][j];
}
//set pointer in BoardPos to the new board
//30 minutes of debugging >.>
BoardPos::board = board;
}
//put a piece into column numbered c
//columns are indexed from 0
//returns whether drop was successful
bool dropPiece(unsigned c) {
//get first unfilled space going down in the column
unsigned i = 0;
if(c > ROW) return 0;
while(i < ROW && isVacant(*BoardPos(i, c)))
++i;
//column is completely filled, return unsuccessful
if(i == 0) return false;
//go back up to unfilled slot
--i;
//fill the slot and flip turns
if(isBlackTurn)
*BoardPos(i, c) = BLACK;
else
*BoardPos(i, c) = RED;
isBlackTurn = !isBlackTurn;
return true;
}
//go through one turn
void play() {
int c;
printBoard();
std::cout << (isBlackTurn ? "Black" : "Red") << "'s turn!" << std::endl;
//get column
std::cout << "Where would you like to drop your piece? ";
//TODO: put this into its own function
while(!(std::cin >> c)) {
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "Error: NaN, try again: ";
}
//attempt to drop and display whether it was successful
if(dropPiece(c - 1))
std::cout << "Piece dropped successfully!" << std::endl;
else
std::cout << "That's not on the board!" << std::endl;
}
//return if position represented by p is within bounds
bool withinBounds(const BoardPos& p) {
return 0 <= p.r && p.r < ROW && 0 <= p.c && p.c < COL;
}
//return which side is the winner
//returns VACANT if there is no winner
int winner() {
typedef BoardPos b;
//iterate through each slot and...
for(int i = 0; i < ROW; i++) {
for(int j = 0; j < COL; j++) {
b p(i, j), q;
//skip if slot is vacant
if(isVacant(*p))
continue;
//check along main diagonal
if(withinBounds(q = (p + b(1, 1))) && isMatching(*p, *q)) {
if(withinBounds(q = (p + b(2, 2))) && isMatching(*p, *q)) {
if(withinBounds(q = (p + b(3, 3))) && isMatching(*p, *q)) {
return *p;
}
}
}
//check downwards
if(withinBounds(q = (p + b(1, 0))) && isMatching(*p, *q)) {
if(withinBounds(q = (p + b(2, 0))) && isMatching(*p, *q)) {
if(withinBounds(q = (p + b(3, 0))) && isMatching(*p, *q)) {
return *p;
}
}
}
//check along anti-diagonal
if(withinBounds(q = (p + b(1, -1))) && isMatching(*p, *q)) {
if(withinBounds(q = (p + b(2, -2))) && isMatching(*p, *q)) {
if(withinBounds(q = (p + b(3, -3))) && isMatching(*p, *q)) {
return *p;
}
}
}
//check rightwards
if(withinBounds(q = (p + b(0, 1))) && isMatching(*p, *q)) {
if(withinBounds(q = (p + b(0, 2))) && isMatching(*p, *q)) {
if(withinBounds(q = (p + b(0, 3))) && isMatching(*p, *q)) {
return *p;
}
}
}
//upwards and leftwards check not needed
}
}
//no slots and directions produce winner
return VACANT;
}
} //namespace con4
/*
example playthrough:
int main() {
con4::initBoard();
while(con4::isVacant(con4::winner()))
con4::play();
con4::printBoard();
std::cout << "Winner: " << (con4::isBlackTurn ? "Red" : "Black") << std::endl;
}
*/
#endif