Пример #1
0
def test_predicate():
    m = Map(5, 5)
    assert not m.is_full()
    for i in range(m.num_rows):
        for j in range(m.num_cols):
            p = Pos(i, j)
            if i == 0 or i == m.num_rows - 1 or j == 0 or j == m.num_cols - 1:
                assert not m.is_inside(p) and not m.is_empty(p) \
                       and not m.is_safe(p)
            else:
                assert m.is_inside(p) and m.is_empty(p) and m.is_safe(p)
    p1, p2, p3 = Pos(1, 1), Pos(2, 2), Pos(3, 3)
    m.point(p1).type = PointType.HEAD_L
    m.point(p2).type = PointType.BODY_VER
    m.point(p3).type = PointType.FOOD
    assert m.is_inside(p1) and not m.is_empty(p1) and not m.is_safe(p1)
    assert m.is_inside(p2) and not m.is_empty(p2) and not m.is_safe(p2)
    assert m.is_inside(p3) and not m.is_empty(p3) and m.is_safe(p3)
    assert not m.is_full()
    for i in range(1, m.num_rows - 1):
        for j in range(1, m.num_cols - 1):
            if i < m.num_rows / 2:
                m.point(Pos(i, j)).type = PointType.HEAD_U
            else:
                m.point(Pos(i, j)).type = PointType.BODY_UR
    assert m.is_full()
Пример #2
0
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")
Пример #3
0
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.__window = GameWindow(
            conf, self.__map, "Snake", self.__snake, 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())))
        self.__solver = globals()[self.__conf.solver_name](self.__snake)
        self.__episode = 1
        self.__init_log_file()

    def run(self):
        if self.__conf.show_gui:
            self.__window.show(self.__game_main)
        else:
            self.__run_batch_episodes()

    def __run_batch_episodes(self):
        STEPS_LIMIT = 500
        episodes = int(input("input the number of episodes bro: "))
        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
        for _ in range(episodes):
            print("Episode %d - " % self.__episode, end="")
            while True:
                self.__game_main()
                if self.__map.is_full():
                    tot_suc += 1
                    tot_suc_steps += self.__snake.steps
                    print("SUC  (steps: %d)" % self.__snake.steps)
                    break
                if self.__snake.dead or self.__snake.steps >= STEPS_LIMIT:
                    print("FAIL  (steps: %d)" % self.__snake.steps)
                    if self.__snake.steps >= STEPS_LIMIT:
                        self.__write_logs()  # Write the last step
                    break
            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
        print("\n[Summary]\nTotal: %d  SUC: %d (%.2f%%)  Avg SUC steps: %d\n" % \
              (self.__episode - 1, tot_suc, 100 * suc_ratio, avg_suc_steps))
        self.__on_exit()

    def __game_main(self):
        """Main function in the game loop."""
        if not self.__map.has_food():
            self.__map.create_rand_food()

        if self.__pause or self.__episode_end():
            return

        if self.__conf.enable_AI:
            self.__update_direc(self.__solver.next_direc())

        if self.__conf.show_gui and self.__snake.direc_next != Direc.NONE:
            self.__write_logs()

        self.__snake.move()

        if self.__episode_end():
            self.__write_logs()  # Write the last step

    def __update_direc(self, new_direc):
        if Direc.opposite(new_direc) != self.__snake.direc:
            self.__snake.direc_next = new_direc
            if self.__pause:
                self.__snake.move()

    def __toggle_pause(self):
        self.__pause = not self.__pause

    def __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()

    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")
Пример #4
0
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")
Пример #5
0
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):
        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 _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")
Пример #6
0
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()
Пример #7
0
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.create_food(Pos(1, 3))
    assert m.has_food()
    s.move(Direc.RIGHT)
    assert not m.has_food()
    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.is_full()
    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.create_food(pos)
        s.move(move_direc[i])
    assert m.is_full()
    assert s.len() == 9 and s.steps == 25 and not s.dead