Skip to content

Instantly share code, notes, and snippets.

@gxm11
Last active March 26, 2021 08:28
Show Gist options
  • Select an option

  • Save gxm11/63940b0a4dd96b10955aa7898e376c8c to your computer and use it in GitHub Desktop.

Select an option

Save gxm11/63940b0a4dd96b10955aa7898e376c8c to your computer and use it in GitHub Desktop.
Another BattleShip Game
import numpy as np
import string
import re
class ShipGame:
def __init__(self, width, height):
self.width = width
self.height = height
def reset(self):
self.ships = self.random_keys(10)
self.turn = 0
self.bias = np.zeros((self.width, self.height))
self.enemy_choices = []
for i in range(self.width):
for j in range(self.height):
if j >= self.height // 2:
self.enemy_choices.append((i, j))
self.enemy_last_choice = self.enemy_choices[np.random.randint(len(self.enemy_choices))]
def random_keys(self, n=10):
keys = {}
a = self.height // 2
for pid in [0, 1]:
pos = np.arange(a * self.width)
np.random.shuffle(pos)
for i in pos[0:n]:
if pid == 1:
x, y = i % self.width, i // self.width
else:
x, y = i % self.width, self.height - 1 - i // self.width
keys[(x, y)] = [pid, 0]
return keys
def current_pid(self):
return self.turn % 2
def action(self, x, y, pid=None):
if pid is None:
pid = self.current_pid()
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
if 0 <= x + dx < self.width and 0 <= y + dy < self.height:
self.bias[x + dx, y + dy] += [1, -1][pid]
for k, v in self.ships.items():
_x, _y = k
_pid, _disable = v
if _x == x and _y == y and _pid != pid and _disable == 0:
v[1] = 1
return True
return False
def enemy_make_action(self, pid):
dv = self.value_diff(pid)
# remove all visible
for pos in self.enemy_choices:
if dv[pos[0], pos[1]] > 0:
self.enemy_choices.remove(pos)
x, y = self.enemy_last_choice
x_shift, y_shift = 0, 0
not_discover = 0
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
if x + dx < 0 or x + dx >= self.width:
continue
if y + dy < 0 or y + dy >= self.height:
continue
if dv[x + dx, y + dy] < 0:
not_discover += 1
x_shift += dx
y_shift += dy
if not_discover > 0:
d = 1 if not_discover > 4 else 2
x += min(d, max(-d, x_shift))
y += min(d, max(-d, y_shift))
x = min(self.width - 1, max(0, x))
y = min(self.height - 1, max(0, y))
return (x, y)
x, y = self.enemy_choices[np.random.randint(len(self.enemy_choices))]
return (x, y)
def update(self):
self.bias = self.bias * 0.9
dv = self.value_diff(0)
for k, v in self.ships.items():
if v[0] == 0 and dv[k[0], k[1]] <= 0:
v[1] = 1
if v[0] == 1 and dv[k[0], k[1]] >= 0:
v[1] = 1
def value(self, pid):
def kernel(dx, dy):
return 1 / (1 + (dx * dx + dy * dy))
map = np.zeros((self.width, self.height))
keys = []
for k, v in self.ships.items():
if v[0] == pid and v[1] == 0:
keys.append(k)
for i in range(self.width):
for j in range(self.height):
map[i, j] = np.sum([kernel(i - k[0], j - k[1]) for k in keys])
return map
def value_diff(self, pid):
v = self.value(0) - self.value(1) + self.bias
return [v, -v][pid]
def render(self, pid=0, targets=[]):
header = ["T%-2d" % self.turn] + [string.ascii_uppercase[i]
for i in range(self.width)]
text = [header]
dv = self.value_diff(pid)
for j in range(self.height):
line = ["%3d" % (j + 1)]
for i in range(self.width):
c = "?"
if (i, j) in targets:
c = "@"
else:
v = self.ships.get((i, j), None)
if v is not None:
_pid, _disable = v
if _pid != pid and _disable == 1:
c = "#"
if _pid == pid and _disable == 1:
c = "#"
if _pid == pid and _disable == 0:
c = "x"
else:
if dv[i, j] >= 0:
c = "."
line.append(c)
text.append(line)
for line in text:
for c in line:
print(c, end=" ")
print("")
def render_ships(self):
print("Ships: ", ", ".join(
"%s%d" % (string.ascii_uppercase[k[0]], k[1] + 1) for k in sorted(self.ships.keys())))
def get_input(self):
str = input("type pos to attack: ").strip()
m = re.match(r"^(\w)(\d+)$", str)
if m is None:
return str
if m.group(1) in string.ascii_uppercase:
x = ord(str[0]) - ord('A')
else:
x = ord(str[0]) - ord('a')
y = int(m.group(2)) - 1
if 0 <= x < self.width and 0 <= y < self.height:
return (x, y)
else:
print("invalid position. (try F8)")
return ""
def winner(self):
for pid in [0, 1]:
flag_winner = pid
for v in self.ships.values():
if v[0] == 1 - pid and v[1] == 0:
flag_winner = None
break
if flag_winner is not None:
return flag_winner
return None
def main(self):
self.reset()
targets = []
while True:
self.update()
if self.winner() is not None:
self.render()
print("Winner is p%d" % self.winner())
break
if self.current_pid() == 0:
self.render()
while True:
inputs = self.get_input()
if type(inputs) == tuple:
x, y = inputs
targets.append((x, y))
break
else:
if inputs in ['exit']:
raise BaseException("Game Terminated.")
elif inputs in ['help', 'h']:
self.render(targets=targets)
else:
print("invalid commands. (try help or h)")
continue
else:
x, y = self.enemy_make_action(self.current_pid())
self.enemy_last_choice = (x, y)
ret = self.action(x, y)
print("p%d attack on %s%d, %s" %
(self.current_pid(), string.ascii_uppercase[x], y + 1, ret))
if not ret:
self.turn += 1
if __name__ == "__main__":
g = ShipGame(12, 12)
while True:
g.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment