def test_dead(): m = Map(5, 5) s = Snake(m, Direc.RIGHT, [Pos(1, 3), Pos(1, 2), Pos(1, 1)], [PointType.HEAD_R, PointType.BODY_HOR, PointType.BODY_HOR]) assert not s.dead s.move(s.direc) assert s.dead and s.len() == 3 and s.head() == Pos(1, 4) m.reset() s = Snake(m, Direc.RIGHT, [Pos(1, 3), Pos(1, 2), Pos(1, 1)], [PointType.HEAD_R, PointType.BODY_HOR, PointType.BODY_HOR]) assert not s.dead s.move(Direc.UP) assert s.dead and s.len() == 3 and s.head() == Pos(0, 3) m.reset() s = Snake(m, Direc.DOWN, [Pos(3, 1), Pos(2, 1), Pos(1, 1)], [PointType.HEAD_D, PointType.BODY_VER, PointType.BODY_VER]) assert not s.dead s.move(s.direc) assert s.dead and s.len() == 3 and s.head() == Pos(4, 1) m.reset() s = Snake(m, Direc.DOWN, [Pos(3, 1), Pos(2, 1), Pos(1, 1)], [PointType.HEAD_D, PointType.BODY_VER, PointType.BODY_VER]) assert not s.dead s.move(Direc.LEFT) assert s.dead and s.len() == 3 and s.head() == Pos(3, 0) m.reset() s = Snake( m, Direc.LEFT, [Pos(2, 2), Pos(3, 2), Pos(3, 1), Pos(2, 1), Pos(1, 1)], [ PointType.HEAD_U, PointType.BODY_LU, PointType.BODY_UR, PointType.BODY_VER, PointType.BODY_VER ]) assert not s.dead s.move(s.direc) assert s.dead and s.len() == 5 and s.head() == Pos(2, 1)
def test_init(): m = Map(5, 5) s = Snake(m, Direc.RIGHT, [Pos(1, 3), Pos(1, 2), Pos(1, 1)], [PointType.HEAD_R, PointType.BODY_HOR, PointType.BODY_HOR]) assert not s.dead assert s.direc is Direc.RIGHT assert s.len() == 3 assert s.head() == Pos(1, 3) assert s.bodies[1] == Pos(1, 2) assert s.tail() == Pos(1, 1) assert m.point(Pos(1, 1)).type == PointType.BODY_HOR assert m.point(Pos(1, 2)).type == PointType.BODY_HOR assert m.point(Pos(1, 3)).type == PointType.HEAD_R
def test_move_eat(): m = Map(5, 5) s = Snake(m, Direc.RIGHT, [Pos(1, 2), Pos(1, 1)], [PointType.HEAD_R, PointType.BODY_HOR]) assert s.len() == 2 m.createFood(Pos(1, 3)) assert m.hasFood() s.move(Direc.RIGHT) assert not m.hasFood() assert s.head() == Pos(1, 3) and s.tail() == Pos(1, 1) assert m.point(s.tail()).type == PointType.BODY_HOR assert m.point(Pos(1, 2)).type == PointType.BODY_HOR assert m.point(s.head()).type == PointType.HEAD_R s.move(Direc.DOWN) assert s.head() == Pos(2, 3) and s.tail() == Pos(1, 2) assert m.point(s.tail()).type == PointType.BODY_HOR assert m.point(Pos(1, 3)).type == PointType.BODY_DL assert m.point(s.head()).type == PointType.HEAD_D s.move(Direc.LEFT) assert s.head() == Pos(2, 2) and s.tail() == Pos(1, 3) assert m.point(s.tail()).type == PointType.BODY_DL assert m.point(Pos(2, 3)).type == PointType.BODY_LU assert m.point(s.head()).type == PointType.HEAD_L s.move(Direc.LEFT) assert s.head() == Pos(2, 1) and s.tail() == Pos(2, 3) assert m.point(s.tail()).type == PointType.BODY_LU assert m.point(Pos(2, 2)).type == PointType.BODY_HOR assert m.point(s.head()).type == PointType.HEAD_L s.move(Direc.DOWN) assert s.head() == Pos(3, 1) and s.tail() == Pos(2, 2) assert m.point(s.tail()).type == PointType.BODY_HOR assert m.point(Pos(2, 1)).type == PointType.BODY_RD assert m.point(s.head()).type == PointType.HEAD_D s.move(Direc.RIGHT) assert s.head() == Pos(3, 2) and s.tail() == Pos(2, 1) assert m.point(s.tail()).type == PointType.BODY_RD assert m.point(Pos(3, 1)).type == PointType.BODY_UR assert m.point(s.head()).type == PointType.HEAD_R s.move(Direc.RIGHT) assert s.head() == Pos(3, 3) and s.tail() == Pos(3, 1) assert m.point(s.tail()).type == PointType.BODY_UR assert m.point(Pos(3, 2)).type == PointType.BODY_HOR assert m.point(s.head()).type == PointType.HEAD_R s.move(Direc.UP) assert s.head() == Pos(2, 3) and s.tail() == Pos(3, 2) assert m.point(s.tail()).type == PointType.BODY_HOR assert m.point(Pos(3, 3)).type == PointType.BODY_LU assert m.point(s.head()).type == PointType.HEAD_U s.move(Direc.LEFT) assert s.head() == Pos(2, 2) and s.tail() == Pos(3, 3) assert m.point(s.tail()).type == PointType.BODY_LU assert m.point(Pos(2, 3)).type == PointType.BODY_DL assert m.point(s.head()).type == PointType.HEAD_L s.move(Direc.LEFT) assert s.head() == Pos(2, 1) and s.tail() == Pos(2, 3) assert m.point(s.tail()).type == PointType.BODY_DL assert m.point(Pos(2, 2)).type == PointType.BODY_HOR assert m.point(s.head()).type == PointType.HEAD_L s.move(Direc.UP) assert s.head() == Pos(1, 1) and s.tail() == Pos(2, 2) assert m.point(s.tail()).type == PointType.BODY_HOR assert m.point(Pos(2, 1)).type == PointType.BODY_UR assert m.point(s.head()).type == PointType.HEAD_U s.move(Direc.RIGHT) assert s.head() == Pos(1, 2) and s.tail() == Pos(2, 1) assert m.point(s.tail()).type == PointType.BODY_UR assert m.point(Pos(1, 1)).type == PointType.BODY_RD assert m.point(s.head()).type == PointType.HEAD_R s.move(Direc.RIGHT) assert s.head() == Pos(1, 3) and s.tail() == Pos(1, 1) assert m.point(s.tail()).type == PointType.BODY_RD assert m.point(Pos(1, 2)).type == PointType.BODY_HOR assert m.point(s.head()).type == PointType.HEAD_R s.move(Direc.DOWN) s.move(Direc.DOWN) assert s.head() == Pos(3, 3) and s.tail() == Pos(1, 3) assert m.point(s.tail()).type == PointType.BODY_DL assert m.point(Pos(2, 3)).type == PointType.BODY_VER assert m.point(s.head()).type == PointType.HEAD_D s.move(Direc.LEFT) s.move(Direc.LEFT) s.move(Direc.UP) s.move(Direc.UP) assert s.head() == Pos(1, 1) and s.tail() == Pos(3, 1) assert m.point(s.tail()).type == PointType.BODY_UR assert m.point(Pos(2, 1)).type == PointType.BODY_VER assert m.point(s.head()).type == PointType.HEAD_U assert s.len() == 3 # Eat full assert not m.isFull() food_pos = [ Pos(1, 2), Pos(2, 2), Pos(3, 2), Pos(3, 3), Pos(2, 3), Pos(1, 3) ] move_direc = [ Direc.RIGHT, Direc.DOWN, Direc.DOWN, Direc.RIGHT, Direc.UP, Direc.UP ] for i, pos in enumerate(food_pos): m.createFood(pos) s.move(move_direc[i]) assert m.isFull() assert s.len() == 9 and s.steps == 25 and not s.dead
class Game: def __init__(self, conf): self._conf = conf self._map = Map(conf.map_rows + 2, conf.map_cols + 2) self._snake = Snake(self._map, conf.init_direc, conf.init_bodies, conf.init_types) self._pause = False self._solver = globals()[self._conf.solver_name](self._snake) self._episode = 1 self._init_log_file() @property def snake(self): return self._snake @property def episode(self): return self._episode def run(self): if self._conf.mode == GameMode.BENCHMARK: self._run_benchmarks() elif self._conf.mode == GameMode.TRAIN_DQN: self._run_dqn_train() self._plot_history() else: window = GameWindow( "Snake", self._conf, self._map, self, self._on_exit, (('<w>', lambda e: self._update_direc(Direc.UP)), ('<a>', lambda e: self._update_direc(Direc.LEFT)), ('<s>', lambda e: self._update_direc(Direc.DOWN)), ('<d>', lambda e: self._update_direc(Direc.RIGHT)), ('<r>', lambda e: self._reset()), ('<space>', lambda e: self._toggle_pause()))) if self._conf.mode == GameMode.NORMAL: window.show(self._game_main_normal) elif self._conf.mode == GameMode.TRAIN_DQN_GUI: window.show(self._game_main_dqn_train) self._plot_history() def _run_benchmarks(self): STEPS_LIMIT = 5000 NUM_EPISODES = int(input("Please input the number of episodes: ")) print("\nMap size: %dx%d" % (self._conf.map_rows, self._conf.map_cols)) print("Solver: %s\n" % self._conf.solver_name[:-6].lower()) tot_len, tot_steps = 0, 0 for _ in range(NUM_EPISODES): print("Episode %d - " % self._episode, end="") while True: self._game_main_normal() if self._map.is_full(): print("FULL (len: %d | steps: %d)" % (self._snake.len(), self._snake.steps)) break elif self._snake.dead: print("DEAD (len: %d | steps: %d)" % (self._snake.len(), self._snake.steps)) break elif self._snake.steps >= STEPS_LIMIT: print("STEP LIMIT (len: %d | steps: %d)" % (self._snake.len(), self._snake.steps)) self._write_logs() # Write the last step break tot_len += self._snake.len() tot_steps += self._snake.steps self._reset() avg_len = tot_len / NUM_EPISODES avg_steps = tot_steps / NUM_EPISODES print("\n[Summary]\nAverage Length: %.2f\nAverage Steps: %.2f\n" % (avg_len, avg_steps)) self._on_exit() def _run_dqn_train(self): try: while not self._game_main_dqn_train(): pass except KeyboardInterrupt: pass except Exception: traceback.print_exc() finally: self._on_exit() def _game_main_dqn_train(self): if not self._map.has_food(): self._map.create_rand_food() if self._pause: return episode_end, learn_end = self._solver.train() if episode_end: self._reset() return learn_end def _game_main_normal(self): if not self._map.has_food(): self._map.create_rand_food() if self._pause or self._is_episode_end(): return self._update_direc(self._solver.next_direc()) if self._conf.mode == GameMode.NORMAL and self._snake.direc_next != Direc.NONE: self._write_logs() self._snake.move() if self._is_episode_end(): self._write_logs() # Write the last step def _plot_history(self): self._solver.plot() def _update_direc(self, new_direc): self._snake.direc_next = new_direc if self._pause: self._snake.move() def _toggle_pause(self): self._pause = not self._pause def _is_episode_end(self): return self._snake.dead or self._map.is_full() def _reset(self): self._snake.reset() self._episode += 1 def _on_exit(self): if self._log_file: self._log_file.close() if self._solver: self._solver.close() def _init_log_file(self): try: os.makedirs("logs") except OSError as e: if e.errno != errno.EEXIST: raise try: self._log_file = None self._log_file = open('logs/snake.log', 'w') except FileNotFoundError: if self._log_file: self._log_file.close() def _write_logs(self): self._log_file.write("[ Episode %d / Step %d ]\n" % \ (self._episode, self._snake.steps)) for i in range(self._map.num_rows): for j in range(self._map.num_cols): pos = Pos(i, j) t = self._map.point(pos).type if t == PointType.EMPTY: self._log_file.write(" ") elif t == PointType.WALL: self._log_file.write("# ") elif t == PointType.FOOD: self._log_file.write("F ") elif t == PointType.HEAD_L or t == PointType.HEAD_U or \ t == PointType.HEAD_R or t == PointType.HEAD_D: self._log_file.write("H ") elif pos == self._snake.tail(): self._log_file.write("T ") else: self._log_file.write("B ") self._log_file.write("\n") self._log_file.write("[ last/next direc: %s/%s ]\n" % \ (self._snake.direc, self._snake.direc_next)) self._log_file.write("\n")
class Game: def __init__(self, conf): self._conf = conf self._map = Map(conf.map_rows + 2, conf.map_cols + 2) self._snake = Snake(self._map, conf.init_direc, conf.init_bodies, conf.init_types) self._pause = False self._solver = globals()[self._conf.solver_name](self._snake) self._episode = 1 self._init_log_file() @property def snake(self): return self._snake @property def episode(self): return self._episode def run(self): if self._conf.mode == GameMode.BENCHMARK: self._run_benchmarks() elif self._conf.mode == GameMode.TRAIN_DQN: self._run_dqn_train() self._plot_history() else: window = GameWindow("Snake", self._conf, self._map, self, self._on_exit, ( ('<w>', lambda e: self._update_direc(Direc.UP)), ('<a>', lambda e: self._update_direc(Direc.LEFT)), ('<s>', lambda e: self._update_direc(Direc.DOWN)), ('<d>', lambda e: self._update_direc(Direc.RIGHT)), ('<r>', lambda e: self._reset()), ('<space>', lambda e: self._toggle_pause()) )) if self._conf.mode == GameMode.NORMAL: window.show(self._game_main_normal) elif self._conf.mode == GameMode.TRAIN_DQN_GUI: window.show(self._game_main_dqn_train) self._plot_history() def _run_benchmarks(self): STEPS_LIMIT = 5000 NUM_EPISODES = int(input("Please input the number of episodes: ")) print("\nMap size: %dx%d" % (self._conf.map_rows, self._conf.map_cols)) print("Solver: %s\n" % self._conf.solver_name[:-6].lower()) tot_len, tot_steps = 0, 0 for _ in range(NUM_EPISODES): print("Episode %d - " % self._episode, end="") while True: self._game_main_normal() if self._map.is_full(): print("FULL (len: %d | steps: %d)" % (self._snake.len(), self._snake.steps)) break elif self._snake.dead: print("DEAD (len: %d | steps: %d)" % (self._snake.len(), self._snake.steps)) break elif self._snake.steps >= STEPS_LIMIT: print("STEP LIMIT (len: %d | steps: %d)" % (self._snake.len(), self._snake.steps)) self._write_logs() # Write the last step break tot_len += self._snake.len() tot_steps += self._snake.steps self._reset() avg_len = tot_len / NUM_EPISODES avg_steps = tot_steps / NUM_EPISODES print("\n[Summary]\nAverage Length: %.2f\nAverage Steps: %.2f\n" % (avg_len, avg_steps)) self._on_exit() def _run_dqn_train(self): try: while not self._game_main_dqn_train(): pass except KeyboardInterrupt: pass except Exception: traceback.print_exc() finally: self._on_exit() def _game_main_dqn_train(self): if not self._map.has_food(): self._map.create_rand_food() if self._pause: return episode_end, learn_end = self._solver.train() if episode_end: self._reset() return learn_end def _game_main_normal(self): if not self._map.has_food(): self._map.create_rand_food() if self._pause or self._is_episode_end(): return self._update_direc(self._solver.next_direc()) if self._conf.mode == GameMode.NORMAL and self._snake.direc_next != Direc.NONE: self._write_logs() self._snake.move() if self._is_episode_end(): self._write_logs() # Write the last step def _plot_history(self): self._solver.plot() def _update_direc(self, new_direc): self._snake.direc_next = new_direc if self._pause: self._snake.move() def _toggle_pause(self): self._pause = not self._pause def _is_episode_end(self): return self._snake.dead or self._map.is_full() def _reset(self): self._snake.reset() self._episode += 1 def _on_exit(self): if self._log_file: self._log_file.close() if self._solver: self._solver.close() def _init_log_file(self): try: os.makedirs("logs") except OSError as e: if e.errno != errno.EEXIST: raise try: self._log_file = None self._log_file = open('logs/snake.log', 'w') except FileNotFoundError: if self._log_file: self._log_file.close() def _write_logs(self): self._log_file.write("[ Episode %d / Step %d ]\n" % \ (self._episode, self._snake.steps)) for i in range(self._map.num_rows): for j in range(self._map.num_cols): pos = Pos(i, j) t = self._map.point(pos).type if t == PointType.EMPTY: self._log_file.write(" ") elif t == PointType.WALL: self._log_file.write("# ") elif t == PointType.FOOD: self._log_file.write("F ") elif t == PointType.HEAD_L or t == PointType.HEAD_U or \ t == PointType.HEAD_R or t == PointType.HEAD_D: self._log_file.write("H ") elif pos == self._snake.tail(): self._log_file.write("T ") else: self._log_file.write("B ") self._log_file.write("\n") self._log_file.write("[ last/next direc: %s/%s ]\n" % \ (self._snake.direc, self._snake.direc_next)) self._log_file.write("\n")
class Game: def __init__(self, conf): self._conf = conf self._map = Map(conf.map_rows + 2, conf.map_cols + 2) self._snake = Snake(self._map, conf.init_direc, conf.init_bodies, conf.init_types) self._pause = False self._solver = globals()[self._conf.solver_name](self._snake) self._episode = 1 @property def snake(self): return self._snake @property def episode(self): return self._episode def run(self): if self._conf.mode == GameMode.BENCHMARK: self._run_benchmarks() else: if self._conf.mode == GameMode.NORMAL: window = GameWindow( "Snake", self._conf, self._map, self, self._on_exit, (('<w>', lambda e: self._update_direc(Direc.UP)), ('<a>', lambda e: self._update_direc(Direc.LEFT)), ('<s>', lambda e: self._update_direc(Direc.DOWN)), ('<d>', lambda e: self._update_direc(Direc.RIGHT)), ('<r>', lambda e: self._reset()), ('<space>', lambda e: self._toggle_pause()))) window.show(self._game_main_normal) def _run_benchmarks(self): STEPS_LIMIT = 3000 NUM_EPISODES = int(input("Please input the number of episodes: ")) print("\nMap size: %dx%d" % (self._conf.map_rows, self._conf.map_cols)) print("Solver: %s\n" % self._conf.solver_name[:-6].lower()) tot_suc, tot_suc_steps = 0, 0 tot_len, tot_steps = 0, 0 for _ in range(NUM_EPISODES): print("Episode %d - " % self._episode, end="") window = GameWindow( "Snake", self._conf, self._map, self, self._on_exit, (('<w>', lambda e: self._update_direc(Direc.UP)), ('<a>', lambda e: self._update_direc(Direc.LEFT)), ('<s>', lambda e: self._update_direc(Direc.DOWN)), ('<d>', lambda e: self._update_direc(Direc.RIGHT)), ('<r>', lambda e: self._reset()), ('<space>', lambda e: self._toggle_pause()))) while True: window.show(self._game_main_normal) if self._map.is_full(): tot_suc += 1 tot_suc_steps += self._snake.steps print("FULL (len: %d | steps: %d)" % (self._snake.len(), self._snake.steps)) break elif self._snake.dead: print("DEAD (len: %d | steps: %d)" % (self._snake.len(), self._snake.steps)) break elif self._snake.steps >= STEPS_LIMIT: print("STEP LIMIT (len: %d | steps: %d)" % (self._snake.len(), self._snake.steps)) break tot_len += self._snake.len() tot_steps += self._snake.steps self._reset() suc_ratio = tot_suc / (self._episode - 1) avg_suc_steps = 0 if tot_suc != 0: avg_suc_steps = tot_suc_steps // tot_suc avg_len = tot_len / NUM_EPISODES avg_steps = tot_steps / NUM_EPISODES print( "\n[Summary]\nAverage Length: %.2f\nAverage Steps: %.2f\nTotal Runs: %d Successful Runs: %d (%.2f%%) \nAvg Successful steps: %d\n" % (avg_len, avg_steps, self._episode - 1, tot_suc, 100 * suc_ratio, avg_suc_steps)) self._on_exit() def _game_main_normal(self): if not self._map.has_food(): self._map.create_rand_food() if self._pause or self._is_episode_end(): return self._update_direc(self._solver.next_direc()) self._snake.move() def _update_direc(self, new_direc): self._snake.direc_next = new_direc if self._pause: self._snake.move() def _toggle_pause(self): self._pause = not self._pause def _is_episode_end(self): return self._snake.dead or self._map.is_full() def _reset(self): self._snake.reset() self._episode += 1 def _on_exit(self): if self._solver: self._solver.close()