algor.cc revision 1.5
1/* $NetBSD: algor.cc,v 1.5 2012/02/29 23:39:53 joerg Exp $ */ 2 3/*- 4 * Copyright (c) 2003 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Christos Zoulas. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32/* 33 * algor.C: Computer algorithm 34 */ 35#include "defs.h" 36RCSID("$NetBSD: algor.cc,v 1.5 2012/02/29 23:39:53 joerg Exp $") 37 38#include "algor.h" 39#include "board.h" 40#include "box.h" 41#include "random.h" 42 43ALGOR::ALGOR(const char c) : PLAYER(c) 44{ 45#ifdef notyet 46 // Single Edges = (x + y) * 2 47 _edge1 = (_b.nx() * _b.ny()) * 2; 48 // Shared Edges = (x * (y - 1)) + ((x - 1) * y) 49 _edge2 = (_b.nx() * (_b.ny() - 1)) + ((_b.nx() - 1) * _b.ny()); 50 // Maximum Edges filled before closure = x * y * 2 51 _maxedge = _b.nx() * _b.ny() * 2; 52#endif 53} 54 55// Find the first closure, i.e. a box that has 3 edges 56int ALGOR::find_closure(size_t& y, size_t& x, int& dir, BOARD& b) 57{ 58 RANDOM rdy(b.ny()), rdx(b.nx()); 59 60 for (y = rdy(); y < b.ny(); y = rdy()) { 61 rdx.clear(); 62 for (x = rdx(); x < b.nx(); x = rdx()) { 63 BOX box(y, x, b); 64 if (box.count() == 3) { 65 for (dir = BOX::first; dir < BOX::last; dir++) 66 if (!box.isset(dir)) 67 return 1; 68 b.abort("find_closure: 3 sided box[%zu,%zu] has no free sides", 69 y, x); 70 } 71 } 72 } 73 return 0; 74} 75 76#if 0 77size_t ALGOR::find_single() 78{ 79 size_t ne; 80 81 // Find the number of single edges in use 82 for (size_t x = 0; x < b.nx(); x++) { 83 BOX tbox(0, x, b); 84 ne += tbox.isset(BOX::top); 85 BOX bbox(b.ny() - 1, x, b); 86 ne += bbox.isset(BOX::bottom); 87 } 88 for (size_t y = 0; y < _b.ny(); y++) { 89 BOX lbox(y, 0, b); 90 ne += lbox.isset(BOX::left); 91 BOX rbox(y,_b.nx() - 1, b); 92 ne += rbox.isset(BOX::right); 93 } 94 return ne; 95} 96#endif 97 98 99// Count a closure, by counting all boxes that we can close in the current 100// move 101size_t ALGOR::count_closure(size_t& y, size_t& x, int& dir, BOARD& b) 102{ 103 size_t i = 0; 104 size_t tx, ty; 105 int tdir, mv; 106 107 while (find_closure(ty, tx, tdir, b)) { 108 if (i == 0) { 109 // Mark the beginning of the closure 110 x = tx; 111 y = ty; 112 dir = tdir; 113 } 114 if ((mv = b.domove(ty, tx, tdir, getWho())) == -1) 115 b.abort("count_closure: Invalid move (%zu, %zu, %d)", y, x, dir); 116 else 117 i += mv; 118 } 119 return i; 120} 121 122 123/* 124 * Find the largest closure, by closing all possible closures. 125 * return the number of boxes closed in the maximum closure, 126 * and the first box of the maximum closure in (x, y, dir) 127 */ 128size_t ALGOR::find_max_closure(size_t& y, size_t& x, int& dir, const BOARD& b) 129{ 130 BOARD nb(b); 131 int maxdir = -1; 132 size_t nbox, maxbox = 0; 133 size_t maxx = ~0, maxy = ~0; 134 size_t tx = 0, ty = 0; /* XXX: GCC */ 135 int tdir = 0; /* XXX: GCC */ 136 137 while ((nbox = count_closure(ty, tx, tdir, nb)) != 0) 138 if (nbox > maxbox) { 139 // This closure is better, update max 140 maxbox = nbox; 141 maxx = tx; 142 maxy = ty; 143 maxdir = tdir; 144 } 145 146 // Return the max found 147 y = maxy; 148 x = maxx; 149 dir = maxdir; 150 return maxbox; 151} 152 153 154// Find if a turn does not result in a capture on the given box 155// and return the direction if found. 156int ALGOR::try_good_turn(BOX& box, size_t y, size_t x, int& dir, BOARD& b) 157{ 158 // Sanity check; we must have a good box 159 if (box.count() >= 2) 160 b.abort("try_good_turn: box[%zu,%zu] has more than 2 sides occupied", 161 y, x); 162 163 // Make sure we don't make a closure in an adjacent box. 164 // We use a random direction to randomize the game 165 RANDOM rd(BOX::last); 166 for (dir = rd(); dir < BOX::last; dir = rd()) 167 if (!box.isset(dir)) { 168 size_t by = y + BOX::edges[dir].y; 169 size_t bx = x + BOX::edges[dir].x; 170 if (!b.bounds(by, bx)) 171 return 1; 172 173 BOX nbox(by, bx, b); 174 if (nbox.count() < 2) 175 return 1; 176 } 177 178 return 0; 179} 180 181 182// Try to find a turn that does not result in an opponent closure, and 183// return it in (x, y, dir); if not found return 0. 184int ALGOR::find_good_turn(size_t& y, size_t& x, int& dir, const BOARD& b) 185{ 186 BOARD nb(b); 187 RANDOM rdy(b.ny()), rdx(b.nx()); 188 189 for (y = rdy(); y < b.ny(); y = rdy()) { 190 rdx.clear(); 191 for (x = rdx(); x < b.nx(); x = rdx()) { 192 BOX box(y, x, nb); 193 if (box.count() < 2 && try_good_turn(box, y, x, dir, nb)) 194 return 1; 195 } 196 } 197 return 0; 198} 199 200// On a box with 2 edges, return the first or the last free edge, depending 201// on the order specified 202int ALGOR::try_bad_turn(BOX& box, size_t& y, size_t& x, int& dir, BOARD& b, 203 int last) 204{ 205 if (4 - box.count() <= last) 206 b.abort("try_bad_turn: Called at [%zu,%zu] for %d with %d", 207 y, x, last, box.count()); 208 for (dir = BOX::first; dir < BOX::last; dir++) 209 if (!box.isset(dir)) { 210 if (!last) 211 return 1; 212 else 213 last--; 214 } 215 return 0; 216} 217 218// Find a box that has 2 edges and return the first free edge of that 219// box or the last free edge of that box 220int ALGOR::find_bad_turn(size_t& y, size_t& x, int& dir, BOARD& b, int last) 221{ 222 RANDOM rdy(b.ny()), rdx(b.nx()); 223 for (y = rdy(); y < b.ny(); y = rdy()) { 224 rdx.clear(); 225 for (x = rdx(); x < b.nx(); x = rdx()) { 226 BOX box(y, x, b); 227 if ((4 - box.count()) > last && 228 try_bad_turn(box, y, x, dir, b, last)) 229 return 1; 230 } 231 } 232 return 0; 233} 234 235size_t ALGOR::find_min_closure1(size_t& y, size_t& x, int& dir, const BOARD& b, 236 int last) 237{ 238 BOARD nb(b); 239 int tdir, mindir = -1, mv; 240 // number of boxes per closure 241 size_t nbox, minbox = nb.nx() * nb.ny() + 1; 242 size_t tx, ty, minx = ~0, miny = ~0; 243 int xdir = 0; /* XXX: GCC */ 244 245 while (find_bad_turn(ty, tx, tdir, nb, last)) { 246 247 // Play a bad move that would cause the opponent's closure 248 if ((mv = nb.domove(ty, tx, tdir, getWho())) != 0) 249 b.abort("find_min_closure1: Invalid move %d (%zu, %zu, %d)", mv, 250 ty, tx, tdir); 251 252 // Count the opponent's closure 253 if ((nbox = count_closure(y, x, xdir, nb)) == 0) 254 b.abort("find_min_closure1: no closure found"); 255 256 if (nbox <= minbox) { 257 // This closure has fewer boxes 258 minbox = nbox; 259 minx = tx; 260 miny = ty; 261 mindir = tdir; 262 } 263 } 264 265 y = miny; 266 x = minx; 267 dir = mindir; 268 return minbox; 269} 270 271 272// Search for the move that makes the opponent close the least number of 273// boxes; returns 1 if a move found, 0 otherwise 274size_t ALGOR::find_min_closure(size_t& y, size_t& x, int& dir, const BOARD& b) 275{ 276 size_t x1, y1; 277 int dir1; 278 size_t count = b.ny() * b.nx() + 1, count1; 279 280 for (size_t i = 0; i < 3; i++) 281 if (count > (count1 = find_min_closure1(y1, x1, dir1, b, i))) { 282 count = count1; 283 y = y1; 284 x = x1; 285 dir = dir1; 286 } 287 288 return count != b.ny() * b.nx() + 1; 289} 290 291// Return a move in (y, x, dir) 292void ALGOR::play(const BOARD& b, size_t& y, size_t& x, int& dir) 293{ 294 // See if we can close the largest closure available 295 if (find_max_closure(y, x, dir, b)) 296 return; 297 298#ifdef notyet 299 size_t sgl = find_single(); 300 size_t dbl = find_double(); 301#endif 302 303 // See if we can play an edge without giving the opponent a box 304 if (find_good_turn(y, x, dir, b)) 305 return; 306 307 // Too bad, find the move that gives the opponent the fewer boxes 308 if (find_min_closure(y, x, dir, b)) 309 return; 310} 311