Last active
December 7, 2019 07:55
-
-
Save shiracamus/d7a5b39aa4408b2ecbf06807f6befb8f to your computer and use it in GitHub Desktop.
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
#!/usr/bin/python3 | |
import curses | |
import locale | |
from random import randint | |
INSTRUCTION = """\ | |
Maneaters Ver 1.3 | |
Mission : マンイーターを消して生き残れ! | |
O -- Maneater, 段階的に追い詰める敵 | |
# -- Rock, 触るとマンイーターや自分が死ぬ | |
@ -- Player, コントロールしてマンイーターを岩に当て、 | |
生き残れ!" | |
Key control: Tenkey: | |
7 8 9 7 8 9 | |
↖ ↑ ↗ ↖ ↑ ↗ | |
u← i→o 4← 5→ 6 | |
↙ ↓ ↘ ↙ ↓ ↘ | |
j k l 1 2 3 | |
'i' と '5' は、マンイーターのみが動き、自分は動かない | |
Good Luck | |
キーを押してスタート。 | |
""" | |
def main(): | |
Game.init() | |
Game.start() | |
Game.end() | |
class Game: | |
def init(): | |
global screen, field | |
locale.setlocale(locale.LC_ALL, '') # UTF-8 を使うためにLC_ALLをセット | |
screen = curses.initscr() | |
curses.noecho() | |
curses.curs_set(0) | |
field = Field(width=40, height=23) | |
def start(): | |
playing = True | |
while playing: | |
Game.instruction() | |
player = Game.setup() | |
Game.play(player) | |
Game.over() | |
playing = Game.ask_replay() | |
def instruction(): | |
screen.clear() | |
screen.addstr(INSTRUCTION) | |
screen.getch() # 一文字キー入力待ち | |
def setup(): | |
field.clear() | |
player = Player(x=field.width//2, y=field.height//2) # 最初に中央配置 | |
Rock.deploy(num=120) | |
Maneater.deploy(num=12) | |
return player | |
def play(player): | |
while player.is_alive() and Maneater.any_alive(): | |
screen.refresh() | |
player.move() | |
if player.is_alive(): | |
Maneater.chase(player) | |
def over(): | |
if not Maneater.any_alive(): | |
screen.addstr(0, 0, "You Win! ", curses.A_BLINK) | |
else: | |
screen.addstr(0, 0, "You Lose ", curses.A_BLINK) | |
screen.refresh() | |
def ask_replay(): | |
screen.addstr(1, 0, "Try Again? [y/n]") | |
while True: | |
answer = screen.getkey() | |
if answer in 'yn': | |
return answer == 'y' | |
def end(): | |
curses.endwin() | |
class Field: | |
SPACE = " " | |
def __init__(self, width, height): | |
self.width = width | |
self.height = height | |
def clear(self): | |
screen.clear() | |
self.characters = [[self.SPACE] * self.width | |
for _ in range(self.height)] | |
def is_space(self, x, y): | |
return self.characters[y][x] == self.SPACE | |
def inside(self, x, y): | |
return 0 <= x < self.width and 0 <= y < self.height | |
def put(self, x, y, character): | |
self.characters[y][x] = character | |
screen.addstr(y, x * 2, str(character)) # 全角文字を使うのでx座標を2倍 | |
def remove(self, x, y): | |
self.put(x, y, self.SPACE) | |
def find_space(self): | |
while True: | |
x, y = randint(0, self.width - 1), randint(0, self.height - 1) | |
if self.is_space(x, y): | |
return x, y | |
class Character: | |
def __init__(self, x, y): | |
self._alive = True | |
self.x = x | |
self.y = y | |
field.put(x, y, self) | |
def dead(self): | |
self._alive = False | |
def is_alive(self): | |
return self._alive | |
def move(self, x, y): | |
if not self.is_alive(): | |
return | |
field.remove(self.x, self.y) | |
self.x = x | |
self.y = y | |
if self.conflict(): | |
self.dead() | |
return | |
field.put(self.x, self.y, self) | |
def conflict(self): | |
return False | |
class Rock(Character): | |
def __str__(self): | |
return "#" | |
@staticmethod | |
def deploy(num): | |
Rock.rocks = [Rock(*field.find_space()) for _ in range(num)] | |
@staticmethod | |
def hit(target): | |
return any(rock.x == target.x and rock.y == target.y | |
for rock in Rock.rocks) | |
class Maneater(Character): | |
def __str__(self): | |
return "O" | |
@staticmethod | |
def deploy(num): | |
Maneater.maneaters = [Maneater(*field.find_space()) for _ in range(num)] | |
@staticmethod | |
def hit(target): | |
return any(maneater.x == target.x and maneater.y == target.y | |
for maneater in Maneater.maneaters | |
if maneater.is_alive() and maneater != target) | |
@staticmethod | |
def any_alive(): | |
return any(maneater.is_alive() for maneater in Maneater.maneaters) | |
@staticmethod | |
def chase(player): | |
for maneater in Maneater.maneaters: | |
maneater.move(player) | |
def move(self, player): | |
if not self.is_alive(): | |
return | |
# プレイヤーを追いかけるように差分を求める | |
dx = max(-1, min(1, player.x - self.x)) | |
dy = max(-1, min(1, player.y - self.y)) | |
super().move(self.x + dx, self.y + dy) | |
if self.is_alive(): | |
player.hit(self) | |
def conflict(self): | |
return Maneater.hit(self) or Rock.hit(self) | |
class Player(Character): | |
MOVE = { # key: (x, y) | |
'7': (-1, -1), '8': (+0, -1), '9': (+1, -1), | |
'4': (-1, +0), '5': (+0, +0), '6': (+1, +0), | |
'1': (-1, +1), '2': (+0, +1), '3': (+1, +1), | |
'u': (-1, +0), 'i': (+0, +0), 'o': (+1, +0), | |
'j': (-1, +1), 'k': (+0, +1), 'l': (+1, +1), | |
} | |
def __str__(self): | |
return ("X", "@")[self.is_alive()] | |
def hit(self, maneater): | |
if self.x == maneater.x and self.y == maneater.y: | |
self.dead() | |
def move(self): | |
key = screen.getkey() | |
dx, dy = self.MOVE.get(key, (0, 0)) | |
x, y = self.x + dx, self.y + dy | |
if field.inside(x, y): | |
super().move(x, y) | |
def conflict(self): | |
return not field.is_space(self.x, self.y) | |
def dead(self): | |
super().dead() | |
field.put(self.x, self.y, self) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment