예제 #1
0
파일: board.py 프로젝트: IDSIA/NewTechnoWar
    def __init__(self, shape: Tuple[int, int], name: str = ''):
        self.name: str = name
        self.shape: Tuple[int, int] = shape

        # matrices filled with -1 so we can use 0-based as index
        self.terrain: np.ndarray = np.zeros(shape, dtype='uint8')
        self.geography: np.ndarray = np.zeros(shape, dtype='uint8')

        self.objectives: Dict[str, Dict[str, Goal]] = {RED: {}, BLUE: {}}
        self.maxTurn = -1

        x, y = shape

        self.limits: Set[Cube] = set(
            [Hex(q, -1).cube() for q in range(-1, y + 1)] +
            [Hex(q, y).cube() for q in range(-1, y + 1)] +
            [Hex(-1, r).cube()
             for r in range(0, y)] + [Hex(x, r).cube() for r in range(0, y)])

        # obstructions to LOS
        self.obstacles: Set[Cube] = set()

        # movement obstructions are considered in the cost
        self.moveCost: Dict[str, np.ndarray] = {
            FigureType.INFANTRY: np.zeros(shape, dtype='float'),
            FigureType.VEHICLE: np.zeros(shape, dtype='float'),
        }

        self.protectionLevel: np.ndarray = np.zeros(shape, dtype='uint8')

        # internal values initialization
        self.addTerrain(self.terrain)
예제 #2
0
    def testMoveInsideShape(self):
        # top left
        dst = Hex(0, 0).cube()
        self.state.moveFigure(self.tank, dst=dst)

        moves = GM.buildMovements(self.board, self.state, self.tank)

        for move in moves:
            d: Cube = move.destination
            x, y = d.tuple()
            self.assertGreaterEqual(x, 0,
                                    f'moves outside of map limits: ({x},{y})')
            self.assertGreaterEqual(y, 0,
                                    f'moves outside of map limits: ({x},{y})')

        # bottom right
        dst = Hex(15, 15).cube()
        self.state.moveFigure(self.tank, dst=dst)

        moves = GM.buildMovements(self.board, self.state, self.tank)

        for move in moves:
            d: Cube = move.destination
            x, y = d.tuple()
            self.assertLess(x, 16, f'moves outside of map limits: ({x},{y})')
            self.assertLess(y, 16, f'moves outside of map limits: ({x},{y})')
예제 #3
0
    def testReachPoint(self):
        x1 = Hex(4, 4).cube()
        x2 = Hex(5, 5).cube()
        g = GoalReachPoint(RED, self.board.shape, [x1.hex()])

        GM.update(self.state)
        self.assertFalse(g.check(self.state),
                         'figure is still in starting position')

        m1 = Move(self.red_tank, [x1])
        m2 = Move(self.red_tank, [x2])

        GM.step(self.board, self.state, m1)
        self.assertFalse(g.check(self.state),
                         'figure moved to goal in this turn')

        GM.step(self.board, self.state, m2)
        self.assertFalse(g.check(self.state), 'figure moved outside of goal')

        GM.step(self.board, self.state, m1)
        self.assertFalse(g.check(self.state),
                         'figure is in position but not not long enough')
        GM.update(self.state)
        self.assertTrue(g.check(self.state),
                        'figure is in position since previous turn')
예제 #4
0
def fillLine(terrain: np.ndarray, start: tuple, end: tuple, kind: int):
    if start[0] == end[0]:
        line = [Hex(start[0], j).cube() for j in range(start[1], end[1] + 1)]
    elif start[1] == end[1]:
        line = [Hex(i, start[1]).cube() for i in range(start[0], end[0] + 1)]
    else:
        line = Hex(t=start).line(Hex(t=end))
    for hex in line:
        terrain[hex.tuple()] = kind
예제 #5
0
    def testLerps(self):
        start = Hex(4, 1).cube()
        end = Hex(4, 6).cube()

        line = start.line(end)

        for i in range(1, 7):
            r = Hex(4, i).cube()
            h = line[i - 1].round()

            self.assertEqual(r, h, f'i={i} h={h} right={r}')
예제 #6
0
    def __init__(self,
                 position: Hex,
                 terrain: Terrain,
                 geography: int,
                 objective: bool,
                 blockLos: bool,
                 color: str = 'white'):
        self.terrain = terrain
        self.geography = geography
        self.objective = objective
        self.blockLos = blockLos
        self.cube = position.cube()
        self.color = color

        self.i, self.j = position.tuple()
        self.x, self.y = pos_to_xy((self.i, self.j))
예제 #7
0
    def testMoveAndResponse(self):
        # check default status
        self.assertFalse(self.inf_1.activated)
        self.assertFalse(self.inf_1.responded)
        self.assertFalse(self.inf_2.activated)
        self.assertFalse(self.inf_2.responded)

        dst1 = Hex(5, 12).cube()
        dst2 = Hex(8, 12).cube()

        m1 = GM.actionMove(self.board,
                           self.state,
                           self.inf_1,
                           destination=dst1)
        r1 = GM.actionRespond(self.board, self.state, self.inf_1,
                              self.target_2, self.inf_1.weapons['AR'])
        m2 = GM.actionMove(self.board,
                           self.state,
                           self.inf_2,
                           destination=dst2)
        r2 = GM.actionRespond(self.board, self.state, self.inf_2,
                              self.target_3, self.inf_1.weapons['AR'])

        # check that attack activate unit
        GM.step(self.board, self.state, m1)

        self.assertTrue(self.inf_1.activated, 'unit should be activated')
        self.assertFalse(self.inf_1.responded,
                         'unit should not have responded')

        # check that unit can responded after move
        GM.step(self.board, self.state, r1)

        self.assertTrue(self.inf_1.activated, 'unit should be activated')
        self.assertTrue(self.inf_1.responded, 'unit should have responded')

        # check that response does not activate the unit
        GM.step(self.board, self.state, r2)

        self.assertFalse(self.inf_2.activated, 'unit should not be activated')
        self.assertTrue(self.inf_2.responded, 'unit should have responded')

        # check that unit can move after response
        GM.step(self.board, self.state, m2)

        self.assertTrue(self.inf_2.activated, 'unit should be activated')
        self.assertTrue(self.inf_2.responded, 'unit should have responded')
예제 #8
0
    def testMoveOutsideShape(self):
        # outside of map
        dst = Hex(-1, -1).cube()
        self.state.moveFigure(self.tank, dst=dst)

        moves = GM.buildMovements(self.board, self.state, self.tank)

        self.assertEqual(len(moves), 0, 'moves outside of the map!')
예제 #9
0
def scroll(board: GameBoard):
    x, y = board.shape

    objectiveMarks = board.getObjectiveMark()

    for i in range(0, x):
        for j in range(0, y):
            p = Hex(i, j)
            x = p.tuple()

            index = board.terrain[x]
            tt = TYPE_TERRAIN[index]

            h = Hexagon(p, tt, board.geography[x],
                        p.cube() in objectiveMarks, tt.blockLos, tt.color)

            yield h
예제 #10
0
def pzoneToHex(zone):
    if zone is None:
        return []

    x, y = np.where(zone > 0)

    for item in zip(x, y):
        h = Hexagon(Hex(t=item), TERRAIN_TYPE['OPEN_GROUND'], 0, False, False)

        yield h
예제 #11
0
    def placeFigures(self, board: GameBoard, state: GameState) -> None:
        """
        Find the initial position for the figure where the state has the best value. The generation of the position is
        done randomly 100 times.

        :param board:   board of the game
        :param state:   the current state
        """
        # select area
        x, y = np.where(state.placement_zone[self.team] > 0)
        figures = state.getFigures(self.team)

        # choose groups of random positions
        indices = [
            np.random.choice(len(x), size=len(figures), replace=False)
            for _ in range(100)
        ]

        scores = []
        s = deepcopy(state)

        for i in range(len(indices)):
            # select a group of positions
            group = indices[i]
            for j in range(len(group)):
                # move each unit to its position
                figure = figures[j]
                dst = Hex(x[group[j]], y[group[j]]).cube()
                s.moveFigure(figure, figure.position, dst)

            score = stateScore(self.team, self.goal_params, board, s)
            scores.append((score, group))

        # choose the better group
        score, group = self.opt(scores)

        for j in range(len(group)):
            figure = figures[j]
            dst = Hex(x[group[j]], y[group[j]]).cube()
            state.moveFigure(figure, dst=dst)

        logger.info(f'{self.team:5}: placed his troops in {group} ({score})')
예제 #12
0
    def testArmoredUnitBlock(self):
        dst = Hex(3, 5).cube()
        m1 = GM.actionMove(self.board, self.state, self.red_tank, destination=dst)

        # we move the tank in a blocking position
        GM.step(self.board, self.state, m1)

        los_on_target = self.state.getLOS(self.blue_tank)
        self.los_inf = los_on_target[self.red_inf.index]

        self.assertFalse(GM.checkLine(self.board, self.state, self.los_inf), 'armored: inf has LOS on target')
예제 #13
0
    def __init__(self, position: tuple or Hex or Cube, name: str, team: str, kind: str, status: FigureStatus = None):
        self.fid = str(uuid.uuid4())
        self.team: str = team
        self.color: str = ''

        self.name: str = name
        self.index: int = -1

        self.kind: str = kind

        self.move: int = 0
        self.load: int = 0
        self.hp: int = 0
        self.hp_max: int = 0

        self.defense: Dict[str, int] = {
            'basic': 1,
            'smoke': 18
        }
        self.weapons: Dict[str, Weapon] = {}

        self.int_atk: int = 0
        self.int_def: int = 0
        self.endurance: int = 0

        self.stat: FigureStatus = status
        self.bonus = 0

        if isinstance(position, Cube):
            self.position: Cube = position
        elif isinstance(position, Hex):
            self.position: Cube = position.cube()
        elif len(position) == 2:
            self.position: Cube = Hex(t=position).cube()
        else:
            self.position: Cube = Cube(t=position)

        self.activated: bool = False
        self.responded: bool = False
        self.attacked: bool = False
        self.moved: bool = False
        self.passed: bool = False
        self.killed: bool = False
        self.hit: bool = False

        self.attacked_by: int = -1

        self.can_transport: bool = False
        self.transport_capacity: int = 0
        self.transporting: List[int] = []
        self.transported_by: int = -1

        self.updates: list = []
예제 #14
0
    def testMoveToDestination(self):
        dst = Hex(4, 4).cube()
        move = GM.actionMove(self.board,
                             self.state,
                             self.tank,
                             destination=dst)

        GM.step(self.board, self.state, move)

        self.assertEqual(
            self.state.getFiguresByPos(move.team, move.destination)[0],
            self.tank, 'figure in the wrong position')
        self.assertEqual(self.tank.stat, stat('IN_MOTION'),
                         'figure should be in motion')
        self.assertEqual(self.tank.position, dst,
                         'figure not at the correct destination')
예제 #15
0
    def testConversion(self):
        for i in range(100):
            for j in range(100):
                h0 = Hex(t=(i, j))
                h1 = Hex(i, j)
                c1 = h1.cube()
                c2 = h0.cube()
                h2 = c1.hex()

                self.assertEqual(h0, h1, f'conversion error: {h0} -> {h1}')
                self.assertEqual(h0, h1, f'conversion error: {c1} -> {c2}')
                self.assertEqual(h1, h2,
                                 f'conversion error: {h1} -> {c2} -> {h2}')
예제 #16
0
    def placeFigures(self, board: GameBoard, state: GameState) -> None:
        """
        Random choose the initial position of the figures.

        :param board:   board of the game
        :param state:   the current state
        """
        # select area
        x, y = np.where(state.placement_zone[self.team] > 0)
        figures = state.getFigures(self.team)

        # choose random positions
        indices = np.random.choice(len(x), size=len(figures), replace=False)

        for i in range(len(figures)):
            # move each unit to its position
            figure = figures[i]
            dst = Hex(x[indices[i]], y[indices[i]]).cube()
            state.moveFigure(figure, figure.position, dst)
예제 #17
0
파일: board.py 프로젝트: IDSIA/NewTechnoWar
    def addTerrain(self, terrain: np.array):
        """
        Sum a terrain matrix to the current board. The values must be of core.Terrain Types.
        Default '0' is 'open ground'.
        """
        self.terrain += terrain

        x, y = self.shape

        # update movement costs, protection level and obstacles
        for i in range(0, x):
            for j in range(0, y):
                index = self.terrain[i, j]
                tt = TYPE_TERRAIN[index]
                self.protectionLevel[i, j] = tt.protectionLevel
                self.moveCost[FigureType.INFANTRY][i, j] = tt.moveCostInf
                self.moveCost[FigureType.VEHICLE][i, j] = tt.moveCostVehicle

                if tt.blockLos:
                    self.obstacles.add(Hex(i, j).cube())
예제 #18
0
    def testActivateMoveToDestination(self):
        dst = Hex(4, 4).cube()
        move = GM.actionMove(self.board,
                             self.state,
                             self.tank,
                             destination=dst)

        self.state1, _ = GM.activate(self.board, self.state, move)
        self.assertNotEqual(
            hash(self.state1), hash(self.state),
            'self.state1 and self.state0 are the same, should be different!')
        self.assertNotEqual(
            self.state.getFigure(move).position,
            self.state1.getFigure(move).position,
            'figure is in teh same location for both self.state0 and self.state1!'
        )

        self.state2, _ = GM.activate(self.board, self.state, move)
        self.assertNotEqual(
            hash(self.state2), hash(self.state),
            'self.state2 and self.state0 are the same, should be different!')
        self.assertEqual(
            self.state1.getFigure(move).position,
            self.state2.getFigure(move).position,
            'self.state1 and self.state2 have different end location')

        self.state3, _ = GM.activate(self.board, self.state, move)
        self.assertNotEqual(
            hash(self.state3), hash(self.state),
            'self.state3 and self.state0 are the same, should be different!')
        self.assertEqual(
            self.state1.getFigure(move).position,
            self.state3.getFigure(move).position,
            'self.state1 and self.state3 have different end location')
        self.assertEqual(
            self.state2.getFigure(move).position,
            self.state3.getFigure(move).position,
            'self.state2 and self.state3 have different end location')
예제 #19
0
    def testShootingGround(self):
        ground = Hex(2, 6).cube()
        attack = GM.actionAttackGround(self.red_tank, ground,
                                       self.red_tank.weapons['SG'])

        GM.step(self.board, self.state, attack)

        self.assertEqual(self.state.smoke.max(), 2, 'cloud with wrong value')
        self.assertEqual(self.state.smoke.sum(), 6,
                         'not enough hex have cloud')

        GM.update(self.state)
        self.assertEqual(self.state.smoke.max(), 1, 'cloud decay not working')

        atk = GM.actionAttack(self.board, self.state, self.blue_tank,
                              self.red_tank, self.red_tank.weapons['CA'])
        outcome = GM.step(self.board, self.state, atk)

        self.assertGreaterEqual(outcome.DEF, 18, 'smoke defense not active')

        GM.update(self.state)
        GM.update(self.state)
        self.assertEqual(self.state.smoke.max(), 0,
                         'cloud not disappearing correctly')
예제 #20
0
    def __init__(self,
                 team: str,
                 shape: tuple,
                 objectives: List[tuple or Hex or Cube],
                 turns: int = 1):
        """
        # TODO: this version support 1 turn maximum!
        """
        super().__init__(team)

        self.objectives: List[Cube] = []

        self.values: np.ndarray = np.zeros(shape)

        self.turns = turns
        # self.entered: Dict[(Cube, int), int] = {}

        for o in objectives:
            if isinstance(o, Hex):
                o = o.cube()
            elif isinstance(o, tuple):
                o = Hex(t=o).cube()
            self.objectives.append(o)

        for x, y in np.ndindex(shape):
            xy = Hex(x, y).cube()
            for o in self.objectives:
                self.values[x, y] = xy.distance(o)

        maxBV = np.max(self.values)

        for x, y in np.ndindex(shape):
            self.values[x, y] = 1 - self.values[x, y] / maxBV

        for o in self.objectives:
            self.values[o.tuple()] = 5
예제 #21
0
    def nextAction(self, board: GameBoard, state: GameState,
                   data: dict) -> None:
        """
        Parse the given data structure in order to get the next action.

        :param board:       board of the game
        :param state:       current state of the game
        :param data:        data received from a human through the web interface
        """
        action = data['action']
        self._clear()

        if action == 'choose':
            self.color = data['color']
            return

        if action == 'place':
            idx = int(data['idx'])
            x = int(data['x'])
            y = int(data['y'])
            pos = Hex(x, y).cube()

            self.place[idx] = pos
            return

        if action == 'wait':
            self.next_action = self.gm.actionWait(self.team)
            return

        if action == 'pass':
            if 'idx' in data and data['team'] == self.team:
                idx = int(data['idx'])
                figure = state.getFigureByIndex(self.team, idx)
                self.next_action = self.gm.actionPassFigure(figure)
            elif data['step'] == 'respond':
                self.next_action = self.gm.actionNoResponse(self.team)
            else:
                self.next_action = self.gm.actionPassTeam(self.team)
            return

        idx = int(data['idx'])
        x = int(data['x'])
        y = int(data['y'])
        pos = Hex(x, y).cube()

        figure = state.getFigureByIndex(self.team, idx)

        if figure.responded and data['step'] == 'respond':
            raise ValueError('Unit has already responded!')

        if figure.activated and data['step'] in ('round', 'move'):
            raise ValueError('Unit has already been activated!')

        if action == 'move':
            fs = state.getFiguresByPos(self.team, pos)
            for transport in fs:
                if transport.canTransport(figure):
                    self.next_action = self.gm.actionLoadInto(
                        board, state, figure, transport)
                    return

            self.next_action = self.gm.actionMove(board,
                                                  state,
                                                  figure,
                                                  destination=pos)
            return

        if action == 'attack':
            w = data['weapon']
            weapon = figure.weapons[w]

            if 'targetTeam' in data:
                targetTeam = data['targetTeam']
                targetIdx = int(data['targetIdx'])
                target = state.getFigureByIndex(targetTeam, targetIdx)

            else:
                otherTeam = BLUE if self.team == RED else RED
                target = state.getFiguresByPos(otherTeam, pos)[
                    0]  # TODO: get unit based on index or weapon target type

            self.next_action = self.gm.actionAttack(board, state, figure,
                                                    target, weapon)
            self.next_response = self.gm.actionRespond(board, state, figure,
                                                       target, weapon)
예제 #22
0
    def testMoveWithTransport(self):
        inf1 = buildFigure('Infantry', (7, 7), RED, 'Inf1', stat('NO_EFFECT'))
        inf2 = buildFigure('Infantry', (7, 8), RED, 'Inf2', stat('NO_EFFECT'))
        inf3 = buildFigure('Infantry', (7, 9), RED, 'Inf3', stat('NO_EFFECT'))

        # add infantry units
        self.state.addFigure(inf1)
        self.state.addFigure(inf2)
        self.state.addFigure(inf3)

        # load 2 units
        load1 = GM.actionLoadInto(self.board, self.state, inf1, self.tank)
        load2 = GM.actionLoadInto(self.board, self.state, inf2, self.tank)
        load3 = GM.actionLoadInto(self.board, self.state, inf3, self.tank)

        GM.step(self.board, self.state, load1)
        GM.step(self.board, self.state, load2)

        # load a third unit: cannot do that!
        self.assertRaises(ValueError, GM.step, self.board, self.state, load3)

        self.assertEqual(inf1.position, self.tank.position)
        self.assertEqual(inf2.position, self.tank.position)
        self.assertNotEqual(inf3.position, self.tank.position)

        # move figure in same position of tank
        move = GM.actionMove(self.board,
                             self.state,
                             inf3,
                             destination=self.tank.position)
        GM.step(self.board, self.state, move)

        figures = self.state.getFiguresByPos(RED, self.tank.position)
        self.assertEqual(len(figures), 4,
                         'not all figures are in the same position')
        self.assertEqual(inf1.transported_by, self.tank.index,
                         'Inf1 not in transporter')
        self.assertEqual(inf2.transported_by, self.tank.index,
                         'Inf2 not in transporter')
        self.assertEqual(inf3.transported_by, -1, 'Inf3 is in transporter')

        # move tank
        dst = Hex(8, 2).cube()
        move = GM.actionMove(self.board,
                             self.state,
                             self.tank,
                             destination=dst)
        GM.step(self.board, self.state, move)

        # figures moves along with tank
        self.assertEqual(inf1.position, self.tank.position,
                         'Inf1 not moved with transporter')
        self.assertEqual(inf2.position, self.tank.position,
                         'Inf2 not moved with transporter')
        self.assertEqual(len(self.tank.transporting), 2,
                         'Transporter not transporting all units')

        self.assertGreater(inf1.transported_by, -1)

        # unload 1 figure
        dst = Hex(8, 4).cube()
        move = GM.actionMove(self.board, self.state, inf1, destination=dst)
        GM.step(self.board, self.state, move)

        self.assertEqual(len(self.tank.transporting), 1,
                         'transporter has less units than expected')
        self.assertNotEqual(
            inf1.position, self.tank.position,
            'Inf1 has not been moved together with transporter')