Last active
March 26, 2021 08:28
-
-
Save gxm11/63940b0a4dd96b10955aa7898e376c8c to your computer and use it in GitHub Desktop.
Another BattleShip Game
This file contains hidden or 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
| 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