Skip to content

Instantly share code, notes, and snippets.

@gxm11
Last active April 18, 2021 05:26
Show Gist options
  • Save gxm11/d41882fb24c60ca3e56efd381e2ce48a to your computer and use it in GitHub Desktop.
Save gxm11/d41882fb24c60ca3e56efd381e2ce48a to your computer and use it in GitHub Desktop.
battleship game with mist
# ship battle game
# 1. ships can generate a detect value to every block nearby, v = 1 / (dx * dx + dy * dy)
# 2. the sea has its own detect limit, which will decrease slightly
# 3. attack will add a temp detect value to 3x3 blocks
# 4. any block where detect value * 2 + temp value > sum(detect value) + detect limit will be visible
import numpy as np
import re
import string
import sys
class Ship_Battle:
def __init__(self, width, height):
self.width = width
self.height = height
def kernel_r2(dx, dy):
return 1 / (1 + dx * dx + dy * dy)
self.kernel = self.kernel_cache(kernel_r2)
def kernel_3x3(dx, dy):
if dx == 0 and dy == 0:
return 8
if abs(dx) <= 1 and abs(dy) <= 1:
return 1
return 0
self.attack = {"normal": self.kernel_cache(kernel_3x3)}
def kernel_cache(self, kernel):
ret = np.zeros((self.width * 2, self.height * 2))
for i in range(self.width * 2):
for j in range(self.height * 2):
ret[i, j] = kernel(i - self.width, j - self.height)
return ret
def reset(self):
self.turn = 0
self.water_level = 1
self.temp_value = np.zeros((self.width, self.height, 2))
self.ships = self.create_ships(10)
self.broken_ships = []
def winner(self):
if len(self.ships[0]) == 0:
if len(self.ships[1]) == 0:
return 2
else:
return 1
elif len(self.ships[1]) == 0:
return 0
else:
return None
def create_ships(self, n):
# p0's ship is in 0 ~ w-1 h/2 ~ h-1
pos = np.arange(self.width * self.height / 2, dtype=int)
np.random.shuffle(pos)
ships_0 = []
for i in pos[0:n]:
_x, _y = i % self.width, i // self.width + self.height // 2
ships_0.append((_x, _y))
# p1's ship is in 0 ~ w-1 0 ~ h/2-1
pos = np.arange(self.width * self.height / 2, dtype=int)
np.random.shuffle(pos)
ships_1 = []
for i in pos[0:n]:
_x, _y = i % self.width, i // self.width
ships_1.append((_x, _y))
return (ships_0, ships_1)
def update(self):
for i, ships in enumerate(self.ships):
value = self.value_int(1 - i)
for _x, _y in ships:
if value[_x, _y] == 1:
ships.remove((_x, _y))
self.broken_ships.append((_x, _y))
self.turn += 1
self.water_level = self.water_level * 0.9
self.temp_value = self.temp_value * 0.9
def action(self, a):
pid, x, y, kind = a
x0, y0 = self.width - x, self.height - y
dv = self.attack[kind][x0:x0 + self.width, y0:y0 + self.height]
self.temp_value[:, :, pid] += dv
def calculate_value(self):
values = np.zeros((self.width, self.height, 2))
for i, ships in enumerate(self.ships):
v = np.zeros((self.width, self.height))
for _x, _y in ships:
x0, y0 = self.width - _x, self.height - _y
v += self.kernel[x0:x0 + self.width, y0:y0 + self.height]
values[:, :, i] = v
return values
def value_int(self, pid):
ret = np.zeros((self.width, self.height), dtype=int)
value = self.calculate_value()
v0 = value[:, :, pid] * 2 + self.temp_value[:, :, pid]
v1 = np.sum(value, axis=2) + self.water_level
ret[v0 >= v1] = 1
return ret
def render(self, pid=0):
template = "T%-2d"
for i in range(self.width):
template += "%2s" % string.ascii_uppercase[i]
for j in range(self.height):
template += "\n"
template += "%3d" % (j + 1)
for i in range(self.width):
template += "%2s"
vars = [self.turn]
value = self.value_int(pid)
for j in range(self.width):
for i in range(self.height):
if (i, j) in self.broken_ships:
c = "#"
elif (i, j) in self.ships[pid]:
c = "x"
else:
if value[i, j] == 1:
if (i, j) in self.ships[1 - pid]:
c = "!"
else:
c = " "
else:
c = "."
vars.append(c)
print(template % tuple(vars))
def render_action(self, a, pid=0):
value = self.value_int(pid)
if value[a[1], a[2]] == 1:
t = (a[0], string.ascii_uppercase[a[1]], a[2] + 1)
print("P%d attack %s%d" % t)
else:
print("P%d attack ?" % a[0])
class Game:
def __init__(self, game):
self.game = game
def run(self):
g = self.game
g.reset()
g.render(0)
self.cpu = None
while g.winner() is None:
a0 = None
while a0 is None:
a0 = self.human_action(g)
a1 = self.cpu_action(g)
g.action(a0)
g.action(a1)
g.update()
g.render_action(a0)
g.render_action(a1)
g.render(0)
if g.winner() == 2:
print("draw")
else:
print("winner is P%d" % g.winner())
def run_test(self):
g = self.game
w = [0, 0, 0, 0]
for i in range(1000):
g.reset()
self.cpu = None
while g.winner() is None:
a = self.human_action_test(g)
a = self.cpu_action(g)
g.action(a)
g.action(a)
g.update()
w[g.winner()] += 1
w[3] += g.turn
w[3] = w[3] / (w[0] + w[1] + w[2])
print("P0 win %d, P1 win %d, Draw %d, Mean Turns = %.2f" % tuple(w))
def human_action(self, game):
str = input("[T%d] Type pos to attack: " % game.turn)
m = re.match(r"^(\w)(\d+)$", str)
if m is None:
return
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
return (0, x, y, "normal")
def human_action_test(self, game):
_x = np.random.randint(game.width)
_y = np.random.randint(game.height // 2)
return (0, _x, _y, "normal")
def cpu_action(self, game):
if self.cpu is None:
pos = []
for i in range(game.width * game.height // 2):
_x, _y = i % game.width, i // game.width + game.height // 2
pos.append((_x, _y))
self.cpu = [pos, None]
pos, last = self.cpu
value = game.value_int(1)
for i, j in pos:
if value[i, j] == 1:
pos.remove((i, j))
if len(pos) > 0:
ix = np.random.randint(len(pos))
action_x, action_y = pos[ix]
else:
_x = np.random.randint(game.width)
_y = np.random.randint(game.height)
return (1, _x, _y, "normal")
if last is not None:
x, y = last
x_shift, y_shift = 0, 0
for i in [-1, 0, 1]:
for j in [-1, 0, 1]:
if 0 <= x + i < game.width and 0 <= y + j < game.height:
if value[x + i, y + j] == 0:
x_shift += i
y_shift += j
x_shift = int((x_shift + 0.5) // 1.5)
y_shift = int((y_shift + 0.5) // 1.5)
if abs(x_shift * y_shift) == 4:
x_shift, y_shift = x_shift / 2, y_shift / 2
_x = x + x_shift
_y = y + y_shift
if (_x, _y) in pos:
action_x, action_y = _x, _y
self.cpu[0].remove((action_x, action_y))
self.cpu[1] = (action_x, action_y)
return (1, action_x, action_y, "normal")
if __name__ == "__main__":
if len(sys.argv) == 2:
if sys.argv[1] == "test":
g = Game(Ship_Battle(12, 12))
g.run_test()
exit()
g = Game(Ship_Battle(12, 12))
while True:
g.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment