def create_rule_book(color, bingo=Bingo.DOUBLE_BINGO, black=6):
    is_left = True
    bonus = 1
    color = color
    block_circle = BlockCirclesCoordinate(is_left, bonus, color, black)
    cross_circle = CrossCirclesCoordinate()

    return RuleBook(block_circle, cross_circle, bingo=bingo)
    def __init__(self, bonus, black, color, is_left=True):
        """
        ブロックサークル内の黒ブロックを運搬する経路を計算するための情報を登録する。
        
        Parameters
        ----------
        bonus : int
            ボーナスサークル番号
        black : int
            ブロックサークル内の黒ブロックが置かれているサークル番号
        color : int
            ブロックサークル内のカラーブロックが置かれているサークル番号
        is_left : bool
            コース設定のためのフラグ
        """
        self.is_left = is_left

        # インスタンス生成
        self.block_circles_solver = BlockCirclesSolver(bonus, black, color,
                                                       is_left)
        self.block_circles_coordinate = BlockCirclesCoordinate(
            is_left, bonus, color, black)

        # 経路を計算
        route_tmp = self.block_circles_solver.solve()
        # 経路の軸の相違を吸収
        self.reverse_route = route_tmp
        self.route = list(map(lambda x: (x[1], x[0]), route_tmp))
        """
        機体の向きの表現方法
        左上               右上
              0   1   2
            +---+---+--
          0 |   | → |    (1, 0)の機体 => (1, 0)
            +---+---+--
          1 | ↑ |   |    (0, 1)の機体 => (0, -1)
            +---+---+--
          2 |   |   |
        左下         右下
        """
        # 機体の向きを初期化
        if self.is_left:
            self.direction = (1, 0)
        else:
            self.direction = (-1, 0)
Exemple #3
0
 def __init__(self, bonus, black, color, is_left):
     """
     ブロックサークル内の黒ブロックを運搬する経路を計算するための情報を登録する。
     
     Parameters
     ----------
     bonus : int
         ボーナスサークル番号
     black : int
         ブロックサークル内の黒ブロックが置かれているサークル番号
     color : int
         ブロックサークル内のカラーブロックが置かれているサークル番号
     is_left : bool
         Lコースかどうか
     """
     self.bonus = bonus
     self.black = black
     self.color = color
     self.is_left = is_left
     self.coordinate = BlockCirclesCoordinate(is_left, bonus, color, black)
Exemple #4
0
def create_block_circles(is_left = True, bonus = 6, color = 3, black = 5):
    return BlockCirclesCoordinate(is_left, bonus, color, black)
Exemple #5
0
class BlockCirclesSolver():
    def __init__(self, bonus, black, color, is_left):
        """
        ブロックサークル内の黒ブロックを運搬する経路を計算するための情報を登録する。
        
        Parameters
        ----------
        bonus : int
            ボーナスサークル番号
        black : int
            ブロックサークル内の黒ブロックが置かれているサークル番号
        color : int
            ブロックサークル内のカラーブロックが置かれているサークル番号
        is_left : bool
            Lコースかどうか
        """
        self.bonus = bonus
        self.black = black
        self.color = color
        self.is_left = is_left
        self.coordinate = BlockCirclesCoordinate(is_left, bonus, color, black)
    
    def solve(self):
        """
        ブロックサークル内の黒ブロック運搬経路を計算する。
        """
        tracks = BlockCirclesTracks(self.coordinate)
        
        # ブロックサークルに進入するサークル番号を計算する
        enter = self.enter_block_circle()
        
        # 黒ブロックを取得するための経路を計算する
        catch_path = self.path_to_catch_block(enter, tracks)
        
        # 黒ブロックが置かれたサークルからボーナスサークルまでの経路を計算する
        placement_path = self.path_to_bonus_circle(tracks)
        
        return [enter] + catch_path + placement_path
            
    def subset_of_tracks(self, start, goal, tracks):
        """
        経路の部分集合を取得する。
        
        Parameters
        ----------
        start : tuple
            始点となるサークルの座標
        goal : tuple
            終点となるサークルの座標
        tracks : list
            経路
        """
        # 始点または終点が経路に存在しない場合は、Noneを返す
        if start not in tracks or goal not in tracks:
            return None

        # 始点は部分集合に含めないために1を足す        
        start_index = tracks.index(start) + 1
        # 終点は部分集合に含めるために1を足す
        goal_index = tracks.index(goal) + 1
        
        # 始点が終点よりも後ろにある場合、始点と経路の末尾、経路の先頭と終点までの部分集合を連結して返す(循環リスト)
        if goal_index < start_index:
            # 始点と経路の末尾までの部分集合を取得する
            back = tracks[start_index : len(tracks)]
            # 経路の先頭と終点までの部分集合を取得する
            front = tracks[0 : goal_index]
            return back + front
            
        return tracks[(start_index) % len(tracks) : goal_index]
    
    def enter_block_circle(self):
        """
        ブロックサークルに進入するサークル番号を計算する。
        """
        # 原則としてLコースのときは、4番サークルに進入する (Rコースのときは、5番サークル)
        enter = self.coordinate.get(4) if self.is_left else self.coordinate.get(5)
        # 代替策としてLコースのときは、6番サークルに進入する (Rコースのときは、8番サークル)
        plan_b = self.coordinate.get(6) if self.is_left else self.coordinate.get(8)
        
        # 進入サークルにカラーブロックが置いてあるかチェックする
        if enter == self.coordinate.get(self.color):
            enter = plan_b
        
        # 代替策のサークルに黒ブロックが置いてあるかチェックする
        if plan_b == self.coordinate.get(self.black):
            enter = plan_b
        
        return enter

    def path_to_catch_block(self, enter, tracks):
        """
        黒ブロックを取得するための経路を計算する。
        
        Parameters
        ----------
        enter : tuple
            進入するサークルの座標
        tracks : list
            経路
        """
        # 原則として内回りの経路を選択する
        path = self.subset_of_tracks(enter, self.coordinate.get(self.black), tracks.inner_tracks)
        
        # 選択した経路内にカラーブロックが置かれているサークルがあるかチェックする
        if self.coordinate.get(self.color) in path:
            path = self.subset_of_tracks(enter, self.coordinate.get(self.black), tracks.outer_tracks)
        
        return path

    def path_to_bonus_circle(self, tracks):
        """
        黒ブロックが置かれたサークルからボーナスサークルまでの経路を計算する。
        
        Parameters
        ----------
        tracks : list
            経路
        """
        # 原則として内回りの経路を選択する
        path = self.subset_of_tracks(self.coordinate.get(self.black), self.coordinate.get(self.bonus), tracks.inner_tracks)
        outer_path = self.subset_of_tracks(self.coordinate.get(self.black), self.coordinate.get(self.bonus), tracks.outer_tracks)
        
        # 選択した経路内にカラーブロックが置かれているサークルがあるかチェックする
        if self.coordinate.get(self.color) in path[0:-1]:
            path = self.subset_of_tracks(self.coordinate.get(self.black), self.coordinate.get(self.bonus), tracks.outer_tracks)
        # 内回り、外回りの両方にもゴールを除いてカラーブロックが置かれていないとき
        elif self.coordinate.get(self.color) not in outer_path[0:-1]:
            # 内回りよりも外回りの方が早い場合は、外回りを採用する
            path = outer_path if len(outer_path) < len(path) else path
        return path
class BlackBlockCommands():
    def __init__(self, bonus, black, color, is_left):
        """
        ブロックサークル内の黒ブロックを運搬する経路を計算するための情報を登録する。
        
        Parameters
        ----------
        bonus : int
            ボーナスサークル番号
        black : int
            ブロックサークル内の黒ブロックが置かれているサークル番号
        color : int
            ブロックサークル内のカラーブロックが置かれているサークル番号
        is_left : bool
            コース設定のためのフラグ
        """
        self.is_left = is_left

        # インスタンス生成
        self.block_circles_solver = BlockCirclesSolver(bonus, black, color, is_left)
        self.block_circles_coordinate = BlockCirclesCoordinate(is_left, bonus, color, black)

        # 経路を計算
        route_tmp = self.block_circles_solver.solve()
        # 経路の軸の相違を吸収
        self.reverse_route = route_tmp
        self.route = list(map(lambda x: (x[1], x[0]), route_tmp))

        """
        機体の向きの表現方法
        左上               右上
              0   1   2
            +---+---+--
          0 |   | → |    (1, 0)の機体 => (1, 0)
            +---+---+--
          1 | ↑ |   |    (0, 1)の機体 => (0, -1)
            +---+---+--
          2 |   |   |
        左下         右下
        """
        # 機体の向きを初期化
        if self.is_left:
            self.direction = (1, 0)
        else:
            self.direction = (-1, 0)

    def gen_commands(self):
        """
        運搬経路からコマンドを生成する

        Returning
        ---------
        コマンドの文字列
        """
        commands = ""
        # ブロックビンゴエリアへの侵入先を決定
        if self.is_left:
            tmp_trans = list(self.block_circles_coordinate.get(4))
            # 座標系の相違を吸収
            tmp_trans[0], tmp_trans[1] = tmp_trans[1], tmp_trans[0]
            if list(self.route[0]) == tmp_trans:
                commands += Instructions.ENTER_BINGO_AREA_L4
            else:
                commands += Instructions.ENTER_BINGO_AREA_L6
        else:
            tmp_trans = list(self.block_circles_coordinate.get(5))
            # 座標系の相違を吸収
            tmp_trans[0], tmp_trans[1] = tmp_trans[1], tmp_trans[0]
            if list(self.route[0]) == tmp_trans:
                commands += Instructions.ENTER_BINGO_AREA_R5
            else:
                commands += Instructions.ENTER_BINGO_AREA_R8
        current_coordinate = self.route[0]
        for i in range(1, len(self.route)):
            commands += self.coordinate_to_command(current_coordinate, self.route[i], self.direction)
            current_coordinate = self.route[i]
        
        # 黒ブロックを配置するコマンドを追加する
        commands = self.put_to_command(commands)
        return commands.replace(Instructions.STRAIGHT * 2, Instructions.STRAIGHT_STRAIGHT)

    def coordinate_to_command(self, robot_coor, next_coor, direction):
        """
        現在の座標と次の座標から動作を計算

        Parameters
        ----------
        robot_coor : tuple
            機体の座標
        next_coor : tuple
            次のの座標(隣の座標でないといけない)
        direction : tuple
            機体の向き
        
        Returning
        ---------
        tmp_commands : str
            一時コマンドの文字列
        """
        # 値のチェック
        if len(robot_coor) != 2:
            raise ValueError("robot_coor is invalid!")
        if len(next_coor) != 2:
            raise ValueError("next_coor is invalid!")
        if len(direction) != 2:
            raise ValueError("direction is invalid!")
        # 斜め移動や隣以外のブロックサークルへ移動するコマンドはコマンドは作成できない
        if abs(robot_coor[0] - next_coor[0]) + abs(robot_coor[1] - next_coor[1]) != 1:
            raise ValueError("Next coordinate value is in valid!")
        tmp_commands = ""
        # 機体の向きを設定
        next_direction = (next_coor[0] - robot_coor[0], next_coor[1] - robot_coor[1])
        tmp_commands += self.direction_to_command(direction, next_direction)
        # 機体の向きを更新
        self.direction = next_direction
        # ブロックサークル間移動
        tmp_commands += Instructions.STRAIGHT
        return tmp_commands

    def direction_to_command(self, robot_direction, movement_direction):
        """
        機体の方向と進行方向からコマンドを計算

        Parameters
        ----------
        robot_direction : tuple
            機体の向き
        movement_direction : tuple
            移動方向
        
        Returning
        ---------
        command : str
        """
        # 値のチェック
        if len(robot_direction) != 2:
            raise ValueError("robot_direction is invalid!")
        if len(movement_direction) != 2:
            raise ValueError("movement_direction is invalid!")
        # 機体の向きと移動方向が一致している場合
        if robot_direction == movement_direction:
            return ""
        # 90度回転
        direction = self.detect_direction(robot_direction, movement_direction)
        if direction == "r":
            return Instructions.SPIN_RIGHT
        if direction == "l":
            return Instructions.SPIN_LEFT
        # 180度右回転
        if robot_direction[0] == movement_direction[0]:
            return Instructions.SPIN180
        if robot_direction[1] == movement_direction[1]:
            return Instructions.SPIN180
        return ""

    def detect_direction(self, robot_direction, movement_direction):
        """
        機体の向きから回転方向を判定する

        Parameters
        ----------
        robot_direction : tuple
            回転前の機体向き
        movement_direction : tuple
            回転後の機体の向き
        
        Returning
        ---------
        direction : str
            右に90度回転時 "r"
            左に90度回転時 "l"
            回転しない or 180度回転時 ""
        """
        # インデックスが増える方向に回転 => 右回転
        dx = (1, 0, -1, 0)
        dy = (0, 1, 0, -1)
        # 回転前のインデックスを取得
        for pre_index in range(len(dx)):
            tmp = (dx[pre_index], dy[pre_index])
            if robot_direction == tmp:
                break
        # 回転後のインデックスを取得
        for next_index in range(len(dx)):
            tmp = (dx[next_index], dy[next_index])
            if movement_direction == tmp:
                break
        # 右に90度回転
        if ((pre_index + 1) % 4) == next_index:
            return 'r'
        # 左に90度回転
        if ((next_index + 1) % 4) == pre_index:
            return 'l'
        # 回転しない or 180度回転
        return ''


    def put_to_command(self, commands):
        """
        ブロックサークル間移動後に走行体がブロックを設置するコマンドを追加する。
        :param commands: ブロックサークル間移動のコマンド
        """
        # 末尾のコマンドを削除する
        commands = commands[:-1]
        # ブロックサークル間の黒線まで移動するコマンドを追加する
        commands += Instructions.PREPARE_TO_PUT
        # 黒線からブロックを設置するコマンドを追加する
        commands += Instructions.PUT

        return commands
def create_commands(is_left=True, bonus=5, color=3, black=6):
    block_circles = BlockCirclesCoordinate(is_left, bonus, color, black)
    cross_circles = CrossCirclesCoordinate()

    return Commands(block_circles, cross_circles)