this repo has no description
1/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2/*
3 * Main authors:
4 * Vincent Barichard <Vincent.Barichard@univ-angers.fr>
5 *
6 * Copyright:
7 * Vincent Barichard, 2013
8 *
9 * Last modified:
10 * $Date$ by $Author$
11 * $Revision$
12 *
13 * This file is part of Quacode:
14 * http://quacode.barichard.com
15 *
16 * Permission is hereby granted, free of charge, to any person obtaining
17 * a copy of this software and associated documentation files (the
18 * "Software"), to deal in the Software without restriction, including
19 * without limitation the rights to use, copy, modify, merge, publish,
20 * distribute, sublicense, and/or sell copies of the Software, and to
21 * permit persons to whom the Software is furnished to do so, subject to
22 * the following conditions:
23 *
24 * The above copyright notice and this permission notice shall be
25 * included in all copies or substantial portions of the Software.
26 *
27 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 *
35 */
36
37#include <iostream>
38#include <vector>
39#include <string>
40
41#include <quacode/qspaceinfo.hh>
42#include <gecode/minimodel.hh>
43#include <gecode/driver.hh>
44
45
46using namespace Gecode;
47
48#ifdef GECODE_HAS_GIST
49namespace Gecode { namespace Driver {
50 /// Specialization for QDFS
51 template<typename S>
52 class GistEngine<QDFS<S> > {
53 public:
54 static void explore(S* root, const Gist::Options& opt) {
55 (void) Gist::explore(root, false, opt);
56 }
57 };
58}}
59#endif
60
61/**
62 * \brief Options taking one additional parameter
63 */
64class ConnectFourOptions : public Options {
65protected:
66 /// Print strategy or not
67 Gecode::Driver::BoolOption _printStrategy;
68 /// Model name
69 Gecode::Driver::StringOption _QCSPmodel;
70 /// Heuristic in branching
71 Gecode::Driver::BoolOption _heuristic;
72 /// File name of recorded moves
73 Gecode::Driver::StringValueOption _file;
74 /// Optional number of rows
75 Gecode::Driver::UnsignedIntOption _row;
76 /// Optional number of cols
77 Gecode::Driver::UnsignedIntOption _col;
78public:
79 /// Initialize options for example with name \a s
80 ConnectFourOptions(const char* s)
81 : Options(s),
82 _printStrategy("-printStrategy","Print strategy",false),
83 _QCSPmodel("-QCSPmodel","Name of the model used for modeling problem",3),
84 _heuristic("-heuristic","Use heuristic when branching (only for model + and ++)",true),
85 _file("-file","File name of recorded moves"),
86 _row("-row","Number of rows (minimum 4)",6),
87 _col("-col","Number of cols (minimum 4)",7) {
88 _QCSPmodel.add(1,"AllState","Model with all states as defined by P. Nightingale. Without Pure Value and heuristic setup.");
89 _QCSPmodel.add(2,"AllState+","Model with all states as defined by P. Nightingale. With cut.");
90 _QCSPmodel.add(3,"AllState++","Model with all states as defined by P. Nightingale. With cut and additional constraints.");
91 add(_printStrategy);
92 add(_QCSPmodel);
93 add(_heuristic);
94 add(_file);
95 add(_row);
96 add(_col);
97 }
98 /// Return true if the strategy must be printed
99 bool printStrategy(void) const {
100 return _printStrategy.value();
101 }
102 /// Return model name
103 int QCSPmodel(void) const {
104 return _QCSPmodel.value();
105 }
106 /// Return if heuristic must be used
107 bool heuristic(void) const {
108 return _heuristic.value();
109 }
110 /// Return file name
111 const char *file(void) const {
112 return _file.value();
113 }
114 /// Return number of rows
115 int row(void) const {
116 return _row.value();
117 }
118 /// Return number of cols
119 int col(void) const {
120 return _col.value();
121 }
122};
123
124/// Succeed the space
125static void gf_success(Space& home) {
126 Space::Branchers b(home);
127 while (b()) {
128 BrancherHandle bh(b.brancher());
129 ++b;
130 bh.kill(home);
131 }
132}
133
134/// Dummy function
135static void gf_dummy(Space& ) { }
136
137/// Adding cut
138static void cut(Space& home, const BoolExpr& expr) {
139 BoolVar o(home,0,1);
140 rel(home, o == expr);
141 when(home, o, &gf_success, &gf_dummy);
142}
143
144template <int N>
145struct c4Heuristic {
146 static int value(const Space& _home, IntVar x, int);
147};
148
149// Template loop to avoid to write a hundred lines of code
150template <int N>
151struct FOR {
152 static void go(IntBranchVal t[]) {
153 t[N] = &c4Heuristic<N>::value;
154 FOR<N-1>::go(t);
155 }
156};
157template <>
158struct FOR<0> {
159 static void go(IntBranchVal t[]) {
160 t[0] = &c4Heuristic<0>::value;
161 }
162};
163
164class ConnectFourAllState : public Script, public QSpaceInfo {
165 static const int Red = 0;
166 static const int Black = 1;
167 static const int Nil = 2;
168
169 IntVarArray M; // Move variables (the column played)
170 IntVarArray U; // Additional move variables (the column played) only usefull for simple model
171 IntVarArray board; // State of board
172 IntVarArray h; // Number of token in col c
173 BoolVarArray mh; // Representing if the move i was made in column c (move-here)
174 IntVarArray gameWinner; // Representing winner 0 = player red wins, 1 = player black wins
175 BoolVarArray line; // Indicating the presence of line in each row, column or diagonal (numbered)
176 BoolVarArray lineMove; // Indicating the presence of line for a move
177 BoolVarArray pos; // Indicating the presence of empty slots
178 BoolVarArray moveDone; // Is true if the move k has been done
179
180 int row;
181 int col;
182 int kOffset;
183
184 const ConnectFourOptions& opt;
185
186public:
187 ConnectFourAllState(const ConnectFourOptions& _opt) : Script(_opt), QSpaceInfo(), opt(_opt)
188 {
189 std::cout << "Loading problem" << std::endl;
190 if (!opt.printStrategy()) strategyMethod(0); // disable build and print strategy
191 using namespace Int;
192 // Define constants
193 row = opt.row();
194 col = opt.col();
195 kOffset = 0;
196 int nbDecisionVar = row*col;
197
198 // Create array of heuristics, one for each brancher
199 assert(nbDecisionVar <= 100);
200 IntBranchVal heuristicArray[100];
201 FOR<100>::go(heuristicArray);
202
203 // Create board variables
204 M = IntVarArray(*this,nbDecisionVar,0,col-1);
205 if (opt.QCSPmodel() == 1) U = IntVarArray(*this,nbDecisionVar/2,0,col-1);
206 board = IntVarArray(*this, nbDecisionVar*row*col, 0, 2);
207 pos = BoolVarArray(*this, nbDecisionVar*row*col, 0, 1);
208 h = IntVarArray(*this, nbDecisionVar*col, 0, row);
209 mh = BoolVarArray(*this, nbDecisionVar*col, 0, 1);
210 lineMove = BoolVarArray(*this, nbDecisionVar, 0, 1);
211 gameWinner = IntVarArray(*this, nbDecisionVar, 0, 2);
212 moveDone = BoolVarArray(*this, nbDecisionVar, 0, 1);
213
214 // Test if a file was given in argument
215 // We will update kOffset according to the file number of moves
216 IntArgs rMoves;
217 if (opt.file()) {
218 std::ifstream f(opt.file());
219
220 if (!f)
221 throw Gecode::Exception("Connect four",
222 "Unable to open file");
223 int move;
224 while (f >> move) {
225 rMoves << move;
226 kOffset++;
227 }
228
229 f.close();
230 assert((kOffset%2) == 0);
231 }
232
233 // Defining the player variables
234 IntVarArgs m, uWm;
235 for (int k=0; k<nbDecisionVar; k++)
236 {
237 // Post brancher
238 if (k >= kOffset) {
239 if ((k%2) == 1) setForAll(*this, M[k]);
240 if (opt.QCSPmodel() == 1)
241 branch(*this, M[k], INT_VAR_NONE(), INT_VALUES_MIN());
242 else if (opt.heuristic())
243 branch(*this, M[k], INT_VAR_NONE(), INT_VAL(heuristicArray[k]));
244 else
245 branch(*this, M[k], INT_VAR_NONE(), INT_VAL_MIN());
246 }
247
248 if (opt.QCSPmodel() == 1) {
249 // Model from P. Nightingale without Pure Value and heuristic setup
250 if ((k%2) == 0) m << M[k];
251 else {
252 if (k >= kOffset) {
253 // With this simple model, we link some new existential variables to
254 // the universal one if the move is legal.
255 // As a result, we increase the number of branched variable and the search space
256 branch(*this, U[k/2], INT_VAR_NONE(), INT_VALUES_MIN());
257 }
258 m << U[k/2];
259 for (int i=0; i < col; i++)
260 rel(*this, ((gameWinner[k-1] == Nil) && (h[(k-1)*col+i] < row) && (M[k] == i)) >> (U[k/2] == i), IPL_DOM); // Forbid illegal move
261 }
262 } else {
263 // Model from P. Nightingale but we add cut and prune universal in order
264 // to achieve same work as Pure Value. To compare with Queso, disable the
265 // heurisitic has we do not have one here.
266 m << M[k];
267 }
268
269 // We build the array of unwatched variables
270 if (((k%2)==0) || (k<kOffset)) uWm << M[k];
271 else uWm << getUnWatched(M[k]);
272
273 // Some moves has been recorded, we play them here
274 if (rMoves.size() > k) rel(*this, uWm[k] == rMoves[k], IPL_DOM);
275
276 // Set the move-here variables
277 if (k==0)
278 for (int i=0; i < col; i++)
279 rel(*this, (m[0] == i) == (mh[0*col+i] && moveDone[0]), IPL_DOM);
280 else {
281 for (int i=0; i < col; i++) {
282 if (opt.QCSPmodel() <= 1) {
283 // Not exactly as the article, we have drop the part with !lineMove[k-1].
284 // We have to do this because it is not compatible with the constraints
285 // which force the last board to be full
286 // rel(*this, (!lineMove[k-1] && (h[(k-1)*col+i] < row) && (m[k] == i)) == mh[k*col+i], IPL_DOM);
287 rel(*this, ((h[(k-1)*col+i] < row) && (m[k] == i)) == mh[k*col+i], IPL_DOM);
288 } else { // opt.QCSPmodel() > 1
289 // rel(*this, (!lineMove[k-1] && (h[(k-1)*col+i] < row) && (m[k] == i)) == (mh[k*col+i] && moveDone[k]), IPL_DOM);
290 rel(*this, ((h[(k-1)*col+i] < row) && (m[k] == i)) == (mh[k*col+i] && moveDone[k]), IPL_DOM);
291 // Prune for universal
292 rel(*this, (h[(k-1)*col+i] == row) >> (uWm[k] != i), IPL_DOM); // Prune illegal move from universal
293
294 // Add cut
295 if ((k%2) == 1) cut(*this, (gameWinner[k-1] == Red) && moveDone[k-1]);
296 }
297 }
298 }
299 }
300
301 // Fill the holes
302 for (int k=0, offSet = 0; k<nbDecisionVar; k++, offSet += row*col)
303 for (int i=0; i < col; i++)
304 for (int j=0; j < row-1; j++) {
305 BoolExpr be;
306 be = expr(*this, board[offSet+i*row+j] != (((k%2)==0)?Black:Red));
307 for (int jj=j+1; jj < row; jj++)
308 be = expr(*this, be && (board[offSet+i*row+jj] == Nil));
309 rel(*this, pos[offSet+i*row+j] == be, IPL_DOM);
310 }
311
312 for (int k=0, offSet = 0; k<nbDecisionVar; k++, offSet += row*col)
313 if (k == 0) {
314 for (int i=0; i < col; i++) {
315 rel(*this, pos[offSet+i*row], IPL_DOM);
316 rel(*this, pos[offSet+i*row+row-1], IPL_DOM);
317 rel(*this, !mh[0*col+i] >> (board[offSet+i*row] == Nil), IPL_DOM);
318 rel(*this, mh[0*col+i] >> (board[offSet+i*row] == Red), IPL_DOM);
319 }
320 } else {
321 for (int i=0; i < col; i++) {
322 rel(*this, (h[(k-1)*col+i] == row) == !pos[offSet+i*row+row-1], IPL_DOM);
323 for (int j=0; j < row; j++) {
324 rel(*this, (h[(k-1)*col+i] == j) >> pos[offSet+i*row+j], IPL_DOM);
325 rel(*this, (!mh[k*col+i] && (h[(k-1)*col+i] == j)) >> (board[offSet+i*row+j] == Nil), IPL_DOM);
326 rel(*this, ( mh[k*col+i] && (h[(k-1)*col+i] == j)) >> (board[offSet+i*row+j] == (((k%2)==0)?Red:Black)), IPL_DOM);
327 }
328 }
329 }
330
331 // Map pieces from board at move i-1 to board at move k
332 for (int k=1, offSet = row*col; k<nbDecisionVar; k++, offSet += row*col)
333 for (int i=0; i < col; i++)
334 for (int j=0; j < row; j++) {
335 rel(*this, (board[(offSet-row*col)+i*row+j] == Black) >> (board[offSet+i*row+j] == Black), IPL_DOM);
336 rel(*this, (board[(offSet-row*col)+i*row+j] == Red) >> (board[offSet+i*row+j] == Red), IPL_DOM);
337 }
338
339 // Link height and board state
340 for (int k=0, offSet = 0; k<nbDecisionVar; k++, offSet += row*col)
341 for (int i=0; i < col; i++)
342 for (int j=0; j < row+1; j++)
343 if (j==0)
344 rel(*this, (board[offSet+i*row] == Nil) >> (h[k*col+i] == 0), IPL_DOM);
345 else if (j==row)
346 rel(*this, (board[offSet+i*row+j-1] != Nil) >> (h[k*col+i] == row), IPL_DOM);
347 else
348 rel(*this, ((board[offSet+i*row+j-1] != Nil) && (board[offSet+i*row+j] == Nil)) >> (h[k*col+i] == j), IPL_DOM);
349
350 // Detect lines
351 BoolVarArgs l;
352 // Detect winning blocks
353 for (int k=0, offSet = 0; k<nbDecisionVar; k++, offSet += row*col) {
354 BoolVarArgs lk;
355 for (int z=0; z<4; z++) { // Row(0) / Col(1) / Diag1(2) / Diag2(3)
356 for (int i=0; i < col; i++) {
357 for (int j=0; j < row; j++) {
358 bool post = false;
359 IntVarArgs x;
360 if (((z%4)==0) && (i+3) < col) { // Line in row
361 x << board[offSet+i*row+j] << board[offSet+(i+1)*row+j] << board[offSet+(i+2)*row+j] << board[offSet+(i+3)*row+j];
362 post = true;
363 }
364 if (((z%4)==1) && (j+3) < row) {// Line in column
365 x << board[offSet+i*row+j] << board[offSet+i*row+j+1] << board[offSet+i*row+j+2] << board[offSet+i*row+j+3];
366 post = true;
367 }
368 if (((z%4)==2) && ((i+3) < col) && ((j+3) < row)) { // Line in diag1
369 x << board[offSet+i*row+j] << board[offSet+(i+1)*row+j+1] << board[offSet+(i+2)*row+j+2] << board[offSet+(i+3)*row+j+3];
370 post = true;
371 }
372 if (((z%4)==3) && ((i-3) >= 0) && ((j+3) < row)) { // Line in diag2
373 x << board[offSet+i*row+j] << board[offSet+(i-1)*row+j+1] << board[offSet+(i-2)*row+j+2] << board[offSet+(i-3)*row+j+3];
374 post = true;
375 }
376 if (post) {
377 if ((k%2) == 0) {
378 BoolVar bRed(*this,0,1);
379 lk << bRed;
380 l << bRed;
381 if (k>0)
382 rel(*this,(lineMove[k-1] || (x[0] != Red) || (x[1] != Red) || (x[2] != Red) || (x[3] != Red)) == !bRed, IPL_DOM);
383 else
384 rel(*this,((x[0] != Red) || (x[1] != Red) || (x[2] != Red) || (x[3] != Red)) == !bRed, IPL_DOM);
385 } else {
386 BoolVar bBlack(*this,0,1);
387 lk << bBlack;
388 l << bBlack;
389 rel(*this,(lineMove[k-1] || (x[0] != Black) || (x[1] != Black) || (x[2] != Black) || (x[3] != Black)) == !bBlack, IPL_DOM);
390 }
391 }
392 }
393 }
394 }
395 if (k>0) lk << lineMove[k-1];
396 rel(*this, BOT_OR, lk, lineMove[k], IPL_DOM);
397 }
398 line = BoolVarArray(*this, l);
399
400 // Set GameState variables
401 rel(*this, gameWinner[0] == Nil, IPL_DOM);
402 for (int k=1; k < nbDecisionVar; k++) {
403 rel(*this, (gameWinner[k-1] == Black) >> (gameWinner[k] == Black), IPL_DOM);
404 rel(*this, (gameWinner[k-1] == Red) >> (gameWinner[k] == Red), IPL_DOM);
405 rel(*this, ((gameWinner[k-1] == Nil) && !lineMove[k]) >> (gameWinner[k] == Nil), IPL_DOM);
406 if ((k%2) == 0)
407 rel(*this, ((gameWinner[k-1] == Nil) && lineMove[k]) >> (gameWinner[k] == Red), IPL_DOM);
408 else
409 rel(*this, ((gameWinner[k-1] == Nil) && lineMove[k]) >> (gameWinner[k] == Black), IPL_DOM);
410
411 if (opt.QCSPmodel() == 3) {
412 // If not winner before, only current player have a chance to win
413 // the game at this move -- NOT IN INITIAL MODEL
414 if ((k%2) == 0)
415 rel(*this, (gameWinner[k-1] == Nil) >> (gameWinner[k] != Black), IPL_DOM);
416 else
417 rel(*this, (gameWinner[k-1] == Nil) >> (gameWinner[k] != Red), IPL_DOM);
418 }
419 }
420
421 // For first move, symmetry is broken by removing the rightmost (upper): col - (col div 2)
422 if (kOffset == 0) rel(*this, m[0], IRT_LE, col - (col / 2), IPL_DOM);
423
424 // Force a winner at the end of the game
425 rel(*this, gameWinner[nbDecisionVar-1], IRT_EQ, Red, IPL_DOM);
426
427 if (opt.QCSPmodel() == 1) {
428 // Set the last board full.
429 // Useless if we prune universal, but needed for the simple model.
430 // Notice that it is not compatible with the -depth argument as all board
431 // doesn't have to be filled.
432 for (int i=0; i < col; i++)
433 for (int j=0; j < row; j++)
434 rel(*this, board[(nbDecisionVar-1)*row*col+i*row+j] != Nil, IPL_DOM);
435 }
436 }
437
438 ConnectFourAllState(bool share, ConnectFourAllState& p)
439 : Script(share,p), QSpaceInfo(*this,share,p), row(p.row), col(p.col), kOffset(p.kOffset), opt(p.opt)
440 {
441 M.update(*this,share,p.M);
442 if (opt.QCSPmodel() == 1) U.update(*this,share,p.U);
443 board.update(*this,share,p.board);
444 h.update(*this,share,p.h);
445 mh.update(*this,share,p.mh);
446 line.update(*this,share,p.line);
447 lineMove.update(*this,share,p.lineMove);
448 pos.update(*this,share,p.pos);
449 gameWinner.update(*this,share,p.gameWinner);
450 moveDone.update(*this,share,p.moveDone);
451 }
452
453 virtual Space* copy(bool share) { return new ConnectFourAllState(share,*this); }
454
455
456 int c4Heuristic(IntVar x, int k) const {
457 if (k == 0) return x.max();
458 std::vector<int> boardBefore(row*col);
459 int offSet = row*col*(k-1);
460 for (int i=row-1; i>=0; i--)
461 for (int j=0; j<col; j++) {
462 assert(board[offSet+j*row+i].assigned());
463 boardBefore[j*row+i] = board[offSet+j*row+i].val();
464 }
465
466 // now we have move number and
467 // previous board state
468 int bestScore=0;
469 int bestMove=x.min();
470
471 for (IntVarValues vv(x); vv(); ++vv) {
472 int j = vv.val();
473 assert(h[(k-1)*col+j].assigned());
474 int i = h[(k-1)*col+j].val();
475
476 if ((i = h[(k-1)*col+j].val()) < row) { // The column is not full
477 boardBefore[j*row+i] = ((k%2)==0)?Red:Black;
478
479 if (((k%2) == 0) && checklines<Red>(boardBefore)) return j; // Leftmost winning move
480 else if (((k%2) == 1) && checklines<Black>(boardBefore)) return j; // Leftmost winning move
481
482 int score;
483 if ((k%2) == 0) { // Red player
484 score = check3lines<Red>(boardBefore);
485 } else { // Black player
486 score = check3lines<Black>(boardBefore);
487 }
488
489 if (score > bestScore) {
490 bestScore = score;
491 bestMove = j;
492 }
493 boardBefore[j*row+i] = Nil;
494 }
495 }
496
497 return bestMove;
498 }
499
500 template <int Player>
501 int check3lines(std::vector<int>& board) const {
502 int lines = 0;
503 // Detect winning blocks
504 for (int z=0; z<4; z++) { // Row(0) / Col(1) / Diag1(2) / Diag2(3)
505 for (int i=0; i < col; i++) {
506 for (int j=0; j < row; j++) {
507 if (((z%4)==0) && (i+3) < col) { // Line in row
508 if (check4for3<Player>(board[i*row+j], board[(i+1)*row+j], board[(i+2)*row+j], board[(i+3)*row+j]))
509 lines++;
510 }
511 if (((z%4)==1) && (j+3) < row) {// Line in column
512 if (check4for3<Player>(board[i*row+j], board[i*row+j+1], board[i*row+j+2], board[i*row+j+3]))
513 lines++;
514 }
515 if (((z%4)==2) && ((i+3) < col) && ((j+3) < row)) { // Line in diag1
516 if (check4for3<Player>(board[i*row+j], board[(i+1)*row+j+1], board[(i+2)*row+j+2], board[(i+3)*row+j+3]))
517 lines++;
518 }
519 if (((z%4)==3) && ((i-3) >= 0) && ((j+3) < row)) { // Line in diag2
520 if (check4for3<Player>(board[i*row+j], board[(i-1)*row+j+1], board[(i-2)*row+j+2], board[(i-3)*row+j+3]))
521 lines++;
522 }
523 }
524 }
525 }
526 return lines;
527 }
528
529 template <int Player>
530 bool check4for3(int a, int b, int c, int d) const {
531 if ((a == Player) && (b == Player) && (c == Player)) return (d == Nil);
532 else if ((a == Player) && (b == Player) && (d == Player)) return (c == Nil);
533 else if ((a == Player) && (c == Player) && (d == Player)) return (b == Nil);
534 else if ((b == Player) && (c == Player) && (d == Player)) return (a == Nil);
535 else return false;
536 }
537
538 template <int Player>
539 bool checklines(std::vector<int>& board) const {
540 // Detect winning blocks
541 for (int z=0; z<4; z++) { // Row(0) / Col(1) / Diag1(2) / Diag2(3)
542 for (int i=0; i < col; i++) {
543 for (int j=0; j < row; j++) {
544 if (((z%4)==0) && (i+3) < col) { // Line in row
545 if ((board[i*row+j] == Player) && (board[(i+1)*row+j] == Player) && (board[(i+2)*row+j] == Player) && (board[(i+3)*row+j] == Player)) return true;
546 }
547 if (((z%4)==1) && (j+3) < row) {// Line in column
548 if ((board[i*row+j] == Player) && (board[i*row+j+1] == Player) && (board[i*row+j+2] == Player) && (board[i*row+j+3] == Player)) return true;
549 }
550 if (((z%4)==2) && ((i+3) < col) && ((j+3) < row)) { // Line in diag1
551 if ((board[i*row+j] == Player) && (board[(i+1)*row+j+1] == Player) && (board[(i+2)*row+j+2] == Player) && (board[(i+3)*row+j+3] == Player)) return true;
552 }
553 if (((z%4)==3) && ((i-3) >= 0) && ((j+3) < row)) { // Line in diag2
554 if ((board[i*row+j] == Player) && (board[(i-1)*row+j+1] == Player) && (board[(i-2)*row+j+2] == Player) && (board[(i-3)*row+j+3] == Player)) return true;
555 }
556 }
557 }
558 }
559 return false;
560 }
561
562 void print(std::ostream& os) const {
563 strategyPrint(os);
564 }
565};
566
567template <int N> int
568c4Heuristic<N>::value(const Space& _home, IntVar x, int) {
569 const ConnectFourAllState& home = static_cast<const ConnectFourAllState&>(_home);
570 return home.c4Heuristic(x,N);
571}
572
573const int ConnectFourAllState::Red;
574const int ConnectFourAllState::Black;
575const int ConnectFourAllState::Nil;
576
577int main(int argc, char* argv[])
578{
579
580 ConnectFourOptions opt("QCSP Connect-Four-Game");
581 opt.parse(argc,argv);
582 Script::run<ConnectFourAllState,QDFS,ConnectFourOptions>(opt);
583
584 return 0;
585}