File size: 5,126 Bytes
70830d6 683d749 70830d6 d2c1e33 70830d6 683d749 70830d6 683d749 70830d6 683d749 70830d6 683d749 70830d6 683d749 70830d6 683d749 70830d6 683d749 70830d6 683d749 d2c1e33 70830d6 d2c1e33 70830d6 683d749 70830d6 683d749 70830d6 683d749 70830d6 683d749 70830d6 683d749 70830d6 683d749 70830d6 683d749 70830d6 683d749 70830d6 |
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 |
from arena.board_view import to_svg
from typing import List
RED = 1
YELLOW = -1
EMPTY = 0
show = {EMPTY: "⚪️", RED: "🔴", YELLOW: "🟡"}
pieces = {EMPTY: "", RED: "red", YELLOW: "yellow"}
simple = {EMPTY: "_", RED: "R", YELLOW: "Y"}
cols = "ABCDEFG"
class Board:
"""
A class to represent a Four-in-the-row Board
"""
def __init__(self):
"""
Initialize this instance, starting with empty cells, RED to play
The latest x,y is used to track the most recent move, so it animates on the display
"""
self.cells = [[0 for _ in range(7)] for _ in range(6)]
self.player = RED
self.winner = EMPTY
self.draw = False
self.forfeit = False
self.latest_x, self.latest_y = -1, -1
def __repr__(self):
"""
A visual representation
"""
result = ""
for y in range(6):
for x in range(7):
result += show[self.cells[5 - y][x]]
result += "\n"
result += "\n" + self.message()
return result
def message(self):
"""
A summary of the status
"""
if self.winner and self.forfeit:
return f"{show[self.winner]} wins after an illegal move by {show[-1*self.winner]}\n"
elif self.winner:
return f"{show[self.winner]} wins\n"
elif self.draw:
return "The game is a draw\n"
else:
return f"{show[self.player]} to play\n"
def html(self):
"""
Return an HTML representation
"""
result = '<div style="text-align: center;font-size:24px">'
result += self.__repr__().replace("\n", "<br/>")
result += "</div>"
return result
def svg(self):
"""
Return an SVG representation
"""
return to_svg(self)
def json(self):
"""
Return a json representation
"""
result = "{\n"
result += ' "Column names": ["A", "B", "C", "D", "E", "F", "G"],\n'
for y in range(6):
result += f' "Row {6-y}": ['
for x in range(7):
result += f'"{pieces[self.cells[5-y][x]]}", '
result = result[:-2] + "],\n"
result = result[:-2] + "\n}"
return result
def alternative(self):
"""
An alternative representation, used in prompting so that the LLM sees this 2 ways
"""
result = " A B C D E F G\n"
for y in range(6):
for x in range(7):
result += " " + simple[self.cells[5 - y][x]]
result += "\n"
return result
def height(self, x: int) -> int:
"""
Return the height of the given column
"""
height = 0
while height < 6 and self.cells[height][x] != EMPTY:
height += 1
return height
def legal_moves(self) -> List[str]:
"""
Return the names of columns that are not full
"""
return [cols[x] for x in range(7) if self.height(x) < 6]
def illegal_moves(self) -> List[str]:
"""
Return the names of columns that are full
"""
return [cols[x] for x in range(7) if self.height(x) == 6]
def winning_line(self, x: int, y: int, dx: int, dy: int) -> int:
"""
Return RED or YELLOW if this cell is the start of a 4 in the row going in the direction dx, dy
Or EMPTY if not
"""
color = self.cells[y][x]
for pointer in range(1, 4):
xp = x + dx * pointer
yp = y + dy * pointer
if not (0 <= xp <= 6 and 0 <= yp <= 5) or self.cells[yp][xp] != color:
return EMPTY
return color
def winning_cell(self, x: int, y: int) -> int:
"""
Return RED or YELLOW if this cell is the start of a 4 in the row
Or EMPTY if not
For performance reasons, only look in 4 of the possible 8 directions,
(because this test will run on both sides of the 4-in-a-row)
"""
for dx, dy in ((0, 1), (1, 1), (1, 0), (1, -1)):
if winner := self.winning_line(x, y, dx, dy):
return winner
return EMPTY
def wins(self) -> int:
"""
Return RED or YELLOW if there is a 4-in-a-row of that color on the board
Or EMPTY if not
"""
for y in range(6):
for x in range(7):
if winner := self.winning_cell(x, y):
return winner
return EMPTY
def move(self, x: int):
"""
Make a move in the given column
"""
y = self.height(x)
self.cells[y][x] = self.player
self.latest_x, self.latest_y = x, y
if winner := self.wins():
self.winner = winner
elif not self.legal_moves:
self.draw = True
else:
self.player = -1 * self.player
return self
def is_active(self) -> bool:
"""
Return true if the game has not yet ended
"""
return not self.winner and not self.draw
|