Last active
June 22, 2016 21:31
-
-
Save gudnm/49e7d75e9841d56a35f4ec71a80fff20 to your computer and use it in GitHub Desktop.
Gomoku game (also called Five in a Row)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Gomoku: | |
def __init__(self, size): | |
self.size = size | |
self.board = ['-'] * size * size | |
self.inarow = 5 | |
self.players = ['x', 'o'] | |
self.curplayer = 1 | |
def printboard(self): | |
s = self.size | |
b = self.board | |
print(' '.join(map(str, range(s+1)))) # print column numbers | |
for i in range(s): | |
# row number plus the row itself | |
# add spaces to make the board look square | |
print(str(i+1) + ' ' + ' '.join(b[i*s : (i+1)*s])) | |
def aimove(self): # IMPROVE! | |
import random | |
placed = False | |
# rudimentary AI, try and place an 'o' closer to most of the 'x's | |
count = 1 | |
avgix = self.size/2 + 1 # initial estimate for row where 'x's congregate | |
avgjx = self.size/2 + 1 # initial estimate for column where 'x's congregate | |
for i in range(self.size): | |
for j in range(self.size): | |
index = i*self.size + j | |
if self.board[index] == 'x': | |
avgix = (avgix*count + i) / (count + 1) | |
avgjx = (avgjx*count + j) / (count + 1) | |
count += 1 | |
sigma = 0.1 # normal distribution, sigma adjusted as space gets crowded | |
while not placed: | |
i, j = -1, -1 | |
while not 0<=i<self.size: | |
i = int(random.gauss(avgix, sigma) + 1) | |
while not 0<=j<self.size: | |
j = int(random.gauss(avgjx, sigma) + 1) | |
index = i*self.size + j | |
if self.board[index] == '-': | |
placed = True | |
sigma += 0.1 | |
return (i, j) | |
def win(self): | |
k = self.inarow | |
n = self.size | |
if n < k: | |
print("The board is too small, we can't play.") | |
return True | |
''' | |
rows | |
0,0 -> k ... 0,n-k | |
... | |
n-1,0 ... n-1,n-k | |
''' | |
for i in range(n): | |
for j in range(n-k+1): | |
index = i*n + j | |
match = True | |
for m in range(k-1): | |
# starting from index, check 5 to the right | |
cur = index + m | |
nxt = index + m+1 | |
if (self.board[cur] != self.board[nxt] | |
or self.board[cur] == '-'): | |
match = False | |
break | |
if match: | |
return True | |
''' | |
columns | |
0,0 ... 0,n-1 | |
| | |
k | |
... | |
n-k,0 ... n-k,n-1 | |
''' | |
for i in range(n-k+1): | |
for j in range(n): | |
index = i*n + j | |
match = True | |
for m in range(k-1): | |
# starting from index, check 5 down | |
cur = index + m*n | |
nxt = index + (m+1)*n | |
if (self.board[cur] != self.board[nxt] | |
or self.board[cur] == '-'): | |
match = False | |
break | |
if match: | |
return True | |
''' | |
forward diagonals | |
0,0 ... 0,n-k | |
\ | |
... k | |
n-k,0 ... n-k,n-k | |
''' | |
for i in range(n-k+1): | |
for j in range(n-k+1): | |
index = i*n + j | |
match = True | |
for m in range(k-1): | |
# starting from index, check 5 down and to the right | |
cur = index + m*(n+1) | |
nxt = index + (m+1)*(n+1) | |
if (self.board[cur] != self.board[nxt] | |
or self.board[cur] == '-'): | |
match = False | |
break | |
if match: | |
return True | |
''' | |
backwards diagonals | |
k | |
/ | |
n-k,0 ... n-k,n-k | |
... | |
n-1,0 ... n-1,n-k | |
''' | |
for i in range(n-k, n): | |
for j in range(n-k+1): | |
index = i*n + j | |
match = True | |
for m in range(k-1): | |
# starting from index, check 5 up and to the right | |
cur = index + m*(-n+1) | |
nxt = index + (m+1)*(-n+1) | |
if (self.board[cur] != self.board[nxt] | |
or self.board[cur] == '-'): | |
match = False | |
break | |
if match: | |
return True | |
return False | |
def getnext(self): | |
i, j = 0, 0 | |
if self.curplayer == 0: # AI's move | |
self.curplayer = 1 | |
player = self.players[self.curplayer] | |
i, j = self.aimove() | |
cell = i*self.size + j | |
self.board[cell] = player | |
gomoku.printboard() | |
else: # human's move | |
self.curplayer = 0 | |
player = self.players[self.curplayer] | |
placed = False | |
while not placed: | |
try: | |
j = int(raw_input("Column # for " + player + ", please: ")) | |
i = int(raw_input("Row # for " + player + ", please: ")) | |
except ValueError: | |
print("Must be a number. Try again?") | |
continue | |
if 0 <= i < self.size and 0 <= j < self.size: | |
index = i*self.size + j | |
if self.board[index] == '-': | |
self.board[index] = player | |
placed = True | |
gomoku.printboard() | |
else: | |
print("That wasn't a valid move. Try again? ") | |
else: | |
print("Must be a number between 1 and " + \ | |
str(self.size + 1) + ". Try again? ") | |
if __name__ == '__main__': | |
assert Gomoku(1) != None | |
assert Gomoku(1).size == 1 | |
assert Gomoku(1).win() == True | |
assert Gomoku(4).win() == True | |
g = Gomoku(5) | |
assert g.win() == False | |
print("All tests passed, let's play.") | |
gomoku = Gomoku(9) | |
gomoku.printboard() | |
while not gomoku.win(): | |
gomoku.getnext() | |
gomoku.printboard() | |
print("Player " + gomoku.players[gomoku.curplayer] + " won the game!") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment