|
| 1 | +''' |
| 2 | +author: Jacob Egner |
| 3 | +date: 2015-08-04 |
| 4 | +island: ice base |
| 5 | +
|
| 6 | +puzzle URLs: |
| 7 | +http://www.checkio.org/mission/place-queens/ |
| 8 | +https://github.com/Bryukh-Checkio-Tasks/checkio-mission-place-queens |
| 9 | +
|
| 10 | +for latest versions of my solutions, see my checkio solution github repo: |
| 11 | +https://github.com/jmegner/CheckioPuzzles |
| 12 | +''' |
| 13 | + |
| 14 | + |
| 15 | +import collections |
| 16 | + |
| 17 | + |
| 18 | +g_boardSize = 8 |
| 19 | + |
| 20 | + |
| 21 | +class Loc(collections.namedtuple('Loc', ['r', 'c'])): |
| 22 | + |
| 23 | + @staticmethod |
| 24 | + def fromChessStr(chessStr): |
| 25 | + return Loc(ord(chessStr[1]) - ord('1'), ord(chessStr[0]) - ord('a')) |
| 26 | + |
| 27 | + |
| 28 | + def toChessStr(self): |
| 29 | + return chr(self.c + ord('a')) + chr(self.r + ord('1')) |
| 30 | + |
| 31 | + |
| 32 | + def posDiag(self): |
| 33 | + return self.r + self.c |
| 34 | + |
| 35 | + |
| 36 | + def negDiag(self): |
| 37 | + return self.r - self.c + g_boardSize - 1 |
| 38 | + |
| 39 | + |
| 40 | + def hasQueenConflict(self, other): |
| 41 | + return( self.r == other.r or self.c == other.c |
| 42 | + or self.posDiag() == other.posDiag() |
| 43 | + or self.negDiag() == other.negDiag() |
| 44 | + ) |
| 45 | + |
| 46 | + |
| 47 | +################################################################################ |
| 48 | +# below is a fairly efficient solution, with simplicity sacrificed |
| 49 | + |
| 50 | +def place_queens(givenQueenStrs): |
| 51 | + queens = [Loc.fromChessStr(queenStr) for queenStr in givenQueenStrs] |
| 52 | + |
| 53 | + rowCounts = [0] * g_boardSize |
| 54 | + colCounts = [0] * g_boardSize |
| 55 | + posDiagCounts = [0] * (2 * g_boardSize - 1) |
| 56 | + negDiagCounts = [0] * (2 * g_boardSize - 1) |
| 57 | + |
| 58 | + for queen in queens: |
| 59 | + rowCounts[queen.r] += 1 |
| 60 | + colCounts[queen.c] += 1 |
| 61 | + posDiagCounts[queen.posDiag()] += 1 |
| 62 | + negDiagCounts[queen.negDiag()] += 1 |
| 63 | + |
| 64 | + for count in rowCounts + colCounts + posDiagCounts + negDiagCounts: |
| 65 | + if count > 1: |
| 66 | + return set() |
| 67 | + |
| 68 | + tryMoreQueens(queens, rowCounts, colCounts, posDiagCounts, negDiagCounts) |
| 69 | + |
| 70 | + if len(queens) == g_boardSize: |
| 71 | + queenSet = set(queen.toChessStr() for queen in queens) |
| 72 | + return queenSet |
| 73 | + |
| 74 | + return set() |
| 75 | + |
| 76 | + |
| 77 | +def tryMoreQueens(queens, rowCounts, colCounts, posDiagCounts, negDiagCounts): |
| 78 | + if len(queens) == g_boardSize: |
| 79 | + return True |
| 80 | + |
| 81 | + for r, rowCount in enumerate(rowCounts): |
| 82 | + if rowCount: |
| 83 | + continue |
| 84 | + |
| 85 | + for c, colCount in enumerate(colCounts): |
| 86 | + if colCount: |
| 87 | + continue |
| 88 | + |
| 89 | + tryLoc = Loc(r, c) |
| 90 | + posDiag = tryLoc.posDiag() |
| 91 | + negDiag = tryLoc.negDiag() |
| 92 | + |
| 93 | + if posDiagCounts[posDiag] or negDiagCounts[negDiag]: |
| 94 | + continue |
| 95 | + |
| 96 | + queens.append(tryLoc) |
| 97 | + rowCounts[r] += 1 |
| 98 | + colCounts[c] += 1 |
| 99 | + posDiagCounts[posDiag] += 1 |
| 100 | + negDiagCounts[negDiag] += 1 |
| 101 | + |
| 102 | + if(tryMoreQueens( |
| 103 | + queens, rowCounts, colCounts, posDiagCounts, negDiagCounts) |
| 104 | + ): |
| 105 | + return True |
| 106 | + |
| 107 | + queens.pop() |
| 108 | + rowCounts[r] -= 1 |
| 109 | + colCounts[c] -= 1 |
| 110 | + posDiagCounts[posDiag] -= 1 |
| 111 | + negDiagCounts[negDiag] -= 1 |
| 112 | + |
| 113 | + return False |
| 114 | + |
| 115 | + |
| 116 | +################################################################################ |
| 117 | +# below is a very simple but fairly inefficient solution |
| 118 | + |
| 119 | +def place_queens_simple(givenQueenStrs): |
| 120 | + givenQueens = [Loc.fromChessStr(queenStr) for queenStr in givenQueenStrs] |
| 121 | + |
| 122 | + for queen1Idx, queen1 in enumerate(givenQueens): |
| 123 | + for queen2 in givenQueens[queen1Idx + 1:]: |
| 124 | + if queen1.hasQueenConflict(queen2): |
| 125 | + return set() |
| 126 | + |
| 127 | + fullQueens = tryMoreQueensSimple(givenQueens) |
| 128 | + |
| 129 | + if len(fullQueens) == g_boardSize: |
| 130 | + queenSet = set(queen.toChessStr() for queen in fullQueens) |
| 131 | + return queenSet |
| 132 | + |
| 133 | + return set() |
| 134 | + |
| 135 | + |
| 136 | +def tryMoreQueensSimple(queens): |
| 137 | + if len(queens) == g_boardSize: |
| 138 | + return queens |
| 139 | + |
| 140 | + for r in range(g_boardSize): |
| 141 | + for c in range(g_boardSize): |
| 142 | + tryLoc = Loc(r, c) |
| 143 | + locHasConflict = False |
| 144 | + |
| 145 | + for queen in queens: |
| 146 | + if tryLoc.hasQueenConflict(queen): |
| 147 | + locHasConflict = True |
| 148 | + break |
| 149 | + |
| 150 | + if not locHasConflict: |
| 151 | + fullQueens = tryMoreQueensSimple(queens + [tryLoc]) |
| 152 | + if fullQueens: |
| 153 | + return fullQueens |
| 154 | + |
| 155 | + return [] |
| 156 | + |
| 157 | + |
| 158 | +if __name__ == '__main__': |
| 159 | + from itertools import combinations |
| 160 | + COLS = "abcdefgh" |
| 161 | + ROWS = "12345678" |
| 162 | + |
| 163 | + THREATS = {c + r: set( |
| 164 | + [c + ROWS[k] for k in range(8)] + |
| 165 | + [COLS[k] + r for k in range(8)] + |
| 166 | + [COLS[k] + ROWS[i - j + k] for k in range(8) if 0 <= i - j + k < 8] + |
| 167 | + [COLS[k] + ROWS[- k + i + j] for k in range(8) if 0 <= - k + i + j < 8]) |
| 168 | + for i, r in enumerate(ROWS) for j, c in enumerate(COLS)} |
| 169 | + |
| 170 | + def check_coordinate(coor): |
| 171 | + c, r = coor |
| 172 | + return c in COLS and r in ROWS |
| 173 | + |
| 174 | + def checker(func, placed, is_possible): |
| 175 | + user_set = func(placed.copy()) |
| 176 | + if not all(isinstance(c, str) and len(c) == 2 and check_coordinate(c) for c in user_set): |
| 177 | + print("Wrong Coordinates") |
| 178 | + return False |
| 179 | + threats = [] |
| 180 | + for f, s in combinations(user_set.union(placed), 2): |
| 181 | + if s in THREATS[f]: |
| 182 | + threats.append([f, s]) |
| 183 | + if not is_possible: |
| 184 | + if user_set: |
| 185 | + print("Hm, how did you place them?") |
| 186 | + return False |
| 187 | + else: |
| 188 | + return True |
| 189 | + if not all(p in user_set for p in placed): |
| 190 | + print("You forgot about placed queens.") |
| 191 | + return False |
| 192 | + if is_possible and threats: |
| 193 | + print("I see some problems in this placement.") |
| 194 | + return False |
| 195 | + return True |
| 196 | + |
| 197 | + assert checker(place_queens, {"b2", "c4", "d6", "e8"}, True), "1st Example" |
| 198 | + assert checker(place_queens, {"b2", "c4", "d6", "e8", "a7", "g5"}, False), "2nd Example" |
| 199 | + assert checker(place_queens, {"a1", "h8"}, False), "Test Extra 2, conflict in givens" |
| 200 | + |
0 commit comments