def test_swapping_with_unintended_intent(self): """ The intent is questionable. England: A Liverpool - Edinburgh F English Channel Convoys A Liverpool - Edinburgh Germany: A Edinburgh - Liverpool France: F Irish Sea Hold F North Sea Hold Russia: F Norwegian Sea Convoys A Liverpool - Edinburgh F North Atlantic Ocean Convoys A Liverpool - Edinburgh See issue 4.A.3. For choice a, b and c the English army in Liverpool will move by convoy and consequentially the two armies are swapped. For choice d, the 1982/2000 rulebook (which I prefer), the convoy depends on the "intent". England intended to convoy via the French fleets in the Irish Sea and the North Sea. However, the French did not order the convoy. The alternative route with the Russian fleets was unintended. The English fleet in the English Channel (with the convoy order) is not part of this alternative route with the Russian fleets. Since England still "intent" to convoy, the move from Liverpool to Edinburgh should be via convoy and the two armies are swapped. Although, you could argue that this is not really according to the clarification of the 2000 rulebook. When explicit adjacent convoying is used (DPTG, choice e), then the English army did not receive an order to move by convoy. So, it is just a head to head battle and both the army in Edinburgh and Liverpool will not move. """ Army(self.state, 0, Nations.ENGLAND, self.territories.LIVERPOOL), Fleet(self.state, 0, Nations.ENGLAND, self.territories.ENGLISH_CHANNEL), Army(self.state, 0, Nations.GERMANY, self.territories.EDINBURGH), Fleet(self.state, 0, Nations.FRANCE, self.territories.IRISH_SEA), Fleet(self.state, 0, Nations.FRANCE, self.territories.NORTH_SEA), Fleet(self.state, 0, Nations.RUSSIA, self.territories.NORWEGIAN_SEA), Fleet(self.state, 0, Nations.RUSSIA, self.territories.NORTH_ATLANTIC), orders = [ Move(self.state, 0, Nations.ENGLAND, self.territories.LIVERPOOL, self.territories.EDINBURGH), Convoy(self.state, 0, Nations.ENGLAND, self.territories.ENGLISH_CHANNEL, self.territories.LIVERPOOL, self.territories.EDINBURGH), Move(self.state, 0, Nations.GERMANY, self.territories.EDINBURGH, self.territories.LIVERPOOL), Hold(self.state, 0, Nations.FRANCE, self.territories.IRISH_SEA), Hold(self.state, 0, Nations.FRANCE, self.territories.NORTH_SEA), Convoy(self.state, 0, Nations.RUSSIA, self.territories.NORWEGIAN_SEA, self.territories.LIVERPOOL, self.territories.EDINBURGH), Convoy(self.state, 0, Nations.RUSSIA, self.territories.NORTH_ATLANTIC, self.territories.LIVERPOOL, self.territories.EDINBURGH), ] process(self.state) self.assertEqual(orders[0].outcome, Outcomes.FAILS) self.assertEqual(orders[2].outcome, Outcomes.FAILS)
def test_piece(self): london = CoastalTerritory(self.state, 1, 'London', 'England', [], []) wales = CoastalTerritory(self.state, 2, 'Wales', 'England', [], []) army = Army(self.state, 0, 'England', london) london_hold = Hold(self.state, 0, 'England', london) wales_hold = Hold(self.state, 0, 'England', wales) self.assertEqual(london_hold.piece, army) self.assertIsNone(wales_hold.piece)
def test_piece(self): state = State() london = CoastalTerritory(1, 'London', 'England', [], []) wales = CoastalTerritory(2, 'Wales', 'England', [], []) army = Army('England', london) london_hold = Hold('England', london) wales_hold = Hold('England', wales) to_register = [london, wales, army, london_hold, wales_hold] [state.register(o) for o in to_register] self.assertEqual(london_hold.piece, army) self.assertIsNone(wales_hold.piece)
def test_support_from_unreachable_coast_not_allowed(self): """ A fleet can not give support to an area that can not be reached from the current coast of the fleet. France: F Marseilles - Gulf of Lyon F Spain(nc) Supports F Marseilles - Gulf of Lyon Italy: F Gulf of Lyon Hold The Gulf of Lyon can not be reached from the North Coast of Spain. Therefore, the support of Spain is invalid and the fleet in the Gulf of Lyon is not dislodged. """ pieces = [ Fleet(Nations.FRANCE, self.territories.MARSEILLES), Fleet(Nations.FRANCE, self.territories.SPAIN, self.named_coasts.SPAIN_NC), Fleet(Nations.ITALY, self.territories.GULF_OF_LYON) ] fleet_marseilles_move = Move(Nations.FRANCE, self.territories.MARSEILLES, self.territories.GULF_OF_LYON) fleet_spain_nc_support = Support(Nations.FRANCE, self.territories.SPAIN, self.territories.MARSEILLES, self.territories.GULF_OF_LYON) fleet_gol_hold = Hold(Nations.ITALY, self.territories.GULF_OF_LYON) self.state.register(*pieces, fleet_marseilles_move, fleet_spain_nc_support, fleet_gol_hold) self.state.post_register_updates() process(self.state) self.assertEqual(fleet_spain_nc_support.legal_decision, Outcomes.ILLEGAL) self.assertEqual(fleet_spain_nc_support.illegal_message, illegal_messages.S002) self.assertEqual(fleet_marseilles_move.move_decision, Outcomes.FAILS) self.assertEqual(pieces[2].dislodged_decision, Outcomes.SUSTAINS)
def test_three_way_beleaguered_garrison(self): """ In a beleaguered garrison from three sides, the adjudicator may not let two attacks fail and then let the third succeed. England: F Edinburgh Supports F Yorkshire - North Sea F Yorkshire - North Sea France: F Belgium - North Sea F English Channel Supports F Belgium - North Sea Germany: F North Sea Hold Russia: F Norwegian Sea - North Sea F Norway Supports F Norwegian Sea - North Sea None of the fleets move. The German fleet in the North Sea is not dislodged. """ pieces = [ Fleet(self.state, 0, Nations.ENGLAND, self.territories.EDINBURGH), Fleet(self.state, 0, Nations.ENGLAND, self.territories.YORKSHIRE), Fleet(self.state, 0, Nations.FRANCE, self.territories.BELGIUM), Fleet(self.state, 0, Nations.FRANCE, self.territories.ENGLISH_CHANNEL), Fleet(self.state, 0, Nations.GERMANY, self.territories.NORTH_SEA), Fleet(self.state, 0, Nations.RUSSIA, self.territories.NORWEGIAN_SEA), Fleet(self.state, 0, Nations.RUSSIA, self.territories.NORWAY), ] orders = [ Support(self.state, 0, Nations.ENGLAND, self.territories.EDINBURGH, self.territories.YORKSHIRE, self.territories.NORTH_SEA), Move(self.state, 0, Nations.ENGLAND, self.territories.YORKSHIRE, self.territories.NORTH_SEA), Move(self.state, 0, Nations.FRANCE, self.territories.BELGIUM, self.territories.NORTH_SEA), Support(self.state, 0, Nations.FRANCE, self.territories.ENGLISH_CHANNEL, self.territories.BELGIUM, self.territories.NORTH_SEA), Hold(self.state, 0, Nations.GERMANY, self.territories.NORTH_SEA), Move(self.state, 0, Nations.RUSSIA, self.territories.NORWEGIAN_SEA, self.territories.NORTH_SEA), Support(self.state, 0, Nations.RUSSIA, self.territories.NORWAY, self.territories.NORWEGIAN_SEA, self.territories.NORTH_SEA), ] process(self.state) self.assertEqual(pieces[4].dislodged_decision, Outcomes.SUSTAINS) self.assertEqual(orders[0].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[1].outcome, Outcomes.FAILS) self.assertEqual(orders[2].outcome, Outcomes.FAILS) self.assertEqual(orders[3].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[5].outcome, Outcomes.FAILS) self.assertEqual(orders[6].outcome, Outcomes.SUCCEEDS)
def test_support_on_unreachable_destination_not_possible(self): """ The destination of the move that is supported must be reachable by the supporting unit. Austria: A Venice Hold Italy: F Rome Supports A Apulia - Venice A Apulia - Venice The support of Rome is illegal, because Venice can not be reached from Rome by a fleet. Venice is not dislodged. """ Army(self.state, 0, Nations.AUSTRIA, self.territories.VENICE), Fleet(self.state, 0, Nations.ITALY, self.territories.ROME), Army(self.state, 0, Nations.ITALY, self.territories.APULIA) # TODO finish Hold(self.state, 0, Nations.AUSTRIA, self.territories.VENICE) fleet_rome_support = Support(self.state, 0, Nations.ITALY, self.territories.ROME, self.territories.APULIA, self.territories.VENICE) Move(self.state, 0, Nations.ITALY, self.territories.APULIA, self.territories.VENICE) process(self.state) self.assertTrue(fleet_rome_support.illegal) self.assertEqual(fleet_rome_support.illegal_code, '010') self.assertEqual( fleet_rome_support.illegal_verbose, 'Piece cannot reach that territory.' )
def test_serialize_state_order(self): london = CoastalTerritory(self.state, 1, 'London', 1, [2], []) Hold(self.state, 1, 1, london) data = TurnSchema().dump(self.state) order_data = (dict(data['orders'][0])) self.assertEqual( sorted(order_data.keys()), ['id', 'illegal', 'illegal_code', 'illegal_verbose', 'outcome'] )
def test_hold(self): london = CoastalTerritory(1, 'London', 'England', [], []) Army('England', london) london_hold = Hold('England', london) self.assertTrue(london_hold.is_hold) self.assertFalse(london_hold.is_move) with self.assertRaises(AttributeError): london_hold.is_fake_class_name
def test_no_self_dislodgement_with_beleauguered_garrison(self): """ An attempt to self dislodge can be combined with a beleaguered garrison. Such self dislodgement is still not possible. England: F North Sea Hold F Yorkshire Supports F Norway - North Sea Germany: F Holland Supports F Helgoland Bight - North Sea F Helgoland Bight - North Sea Russia: F Skagerrak Supports F Norway - North Sea F Norway - North Sea Although the Russians beat the German attack (with the support of Yorkshire) and the two Russian fleets are enough to dislodge the fleet in the North Sea, the fleet in the North Sea is not dislodged, since it would not be dislodged if the English fleet in Yorkshire would not give support. According to the DPTG the fleet in the North Sea would be dislodged. The DPTG is incorrect in this case. """ pieces = [ Fleet(0, Nations.ENGLAND, self.territories.NORTH_SEA), Fleet(0, Nations.ENGLAND, self.territories.YORKSHIRE), Fleet(0, Nations.GERMANY, self.territories.HOLLAND), Fleet(0, Nations.GERMANY, self.territories.HELGOLAND_BIGHT), Fleet(0, Nations.RUSSIA, self.territories.SKAGERRAK), Fleet(0, Nations.RUSSIA, self.territories.NORWAY), ] orders = [ Hold(0, Nations.ENGLAND, self.territories.NORTH_SEA), Support(0, Nations.ENGLAND, self.territories.YORKSHIRE, self.territories.NORWAY, self.territories.NORTH_SEA), Support(0, Nations.GERMANY, self.territories.HOLLAND, self.territories.HELGOLAND_BIGHT, self.territories.NORTH_SEA), Move(0, Nations.GERMANY, self.territories.HELGOLAND_BIGHT, self.territories.NORTH_SEA), Support(0, Nations.RUSSIA, self.territories.SKAGERRAK, self.territories.NORWAY, self.territories.NORTH_SEA), Move(0, Nations.RUSSIA, self.territories.NORWAY, self.territories.NORTH_SEA), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(pieces[0].dislodged_decision, Outcomes.SUSTAINS) self.assertEqual(orders[2].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[3].outcome, Outcomes.FAILS) self.assertEqual(orders[4].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[5].outcome, Outcomes.FAILS)
def test_support_can_be_cut_with_other_coast(self): """ Support can be cut from the other coast. England: F Irish Sea Supports F North Atlantic Ocean - Mid-Atlantic Ocean F North Atlantic Ocean - Mid-Atlantic Ocean France: F Spain(nc) Supports F Mid-Atlantic Ocean F Mid-Atlantic Ocean Hold Italy: F Gulf of Lyon - Spain(sc) The Italian fleet in the Gulf of Lyon will cut the support in Spain. That means that the French fleet in the Mid Atlantic Ocean will be dislodged by the English fleet in the North Atlantic Ocean. """ pieces = [ Fleet(self.state, 0, Nations.ENGLAND, self.territories.NORTH_ATLANTIC), Fleet(self.state, 0, Nations.ENGLAND, self.territories.IRISH_SEA), Fleet(self.state, 0, Nations.FRANCE, self.territories.SPAIN, named_coast=self.named_coasts.SPAIN_NC), Fleet(self.state, 0, Nations.FRANCE, self.territories.MID_ATLANTIC), Fleet(self.state, 0, Nations.ITALY, self.territories.GULF_OF_LYON), ] orders = [ Move(self.state, 0, Nations.ENGLAND, self.territories.NORTH_ATLANTIC, self.territories.MID_ATLANTIC), Support(self.state, 0, Nations.ENGLAND, self.territories.IRISH_SEA, self.territories.NORTH_ATLANTIC, self.territories.MID_ATLANTIC), Support(self.state, 0, Nations.FRANCE, self.territories.SPAIN, self.territories.MID_ATLANTIC, self.territories.MID_ATLANTIC), Hold(self.state, 0, Nations.FRANCE, self.territories.MID_ATLANTIC), Move(self.state, 0, Nations.ITALY, self.territories.GULF_OF_LYON, self.territories.SPAIN, self.named_coasts.SPAIN_SC), ] process(self.state) self.assertEqual(orders[0].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.FAILS) self.assertEqual(orders[4].outcome, Outcomes.FAILS) self.assertEqual(pieces[3].dislodged_decision, Outcomes.DISLODGED)
def test_register_order_updates_piece_order(self): state = State() paris = InlandTerritory(1, 'paris', Nations.FRANCE, []) army_paris = Army(0, Nations.FRANCE, paris) hold = Hold(0, Nations.FRANCE, paris) state.register(paris) state.register(army_paris) state.register(hold) self.assertEqual(army_paris.order, hold) self.assertEqual(hold.piece, army_paris)
def test_support_from_unreachable_coast_not_allowed(self): """ A fleet can not give support to an area that can not be reached from the current coast of the fleet. France: F Marseilles - Gulf of Lyon F Spain(nc) Supports F Marseilles - Gulf of Lyon Italy: F Gulf of Lyon Hold The Gulf of Lyon can not be reached from the North Coast of Spain. Therefore, the support of Spain is invalid and the fleet in the Gulf of Lyon is not dislodged. """ pieces = [ Fleet(self.state, 0, Nations.FRANCE, self.territories.MARSEILLES), Fleet(self.state, 0, Nations.FRANCE, self.territories.SPAIN, named_coast=self.named_coasts.SPAIN_NC), Fleet(self.state, 0, Nations.ITALY, self.territories.GULF_OF_LYON) ] fleet_marseilles_move = Move(self.state, 0, Nations.FRANCE, self.territories.MARSEILLES, self.territories.GULF_OF_LYON) fleet_spain_nc_support = Support(self.state, 0, Nations.FRANCE, self.territories.SPAIN, self.territories.MARSEILLES, self.territories.GULF_OF_LYON) Hold(self.state, 0, Nations.ITALY, self.territories.GULF_OF_LYON) process(self.state) self.assertTrue(fleet_spain_nc_support.illegal) self.assertEqual(fleet_spain_nc_support.illegal_code, '010') self.assertEqual(fleet_spain_nc_support.illegal_verbose, 'Piece cannot reach that territory.') self.assertEqual(fleet_marseilles_move.outcome, Outcomes.FAILS) self.assertEqual(pieces[2].dislodged_decision, Outcomes.SUSTAINS)
def test_support_on_unreachable_destination_not_possible(self): """ The destination of the move that is supported must be reachable by the supporting unit. Austria: A Venice Hold Italy: F Rome Supports A Apulia - Venice A Apulia - Venice The support of Rome is illegal, because Venice can not be reached from Rome by a fleet. Venice is not dislodged. """ pieces = [ Army(Nations.AUSTRIA, self.territories.VENICE), Fleet(Nations.ITALY, self.territories.ROME), Army(Nations.ITALY, self.territories.APULIA) ] # TODO finish army_austria_hold = Hold(Nations.AUSTRIA, self.territories.VENICE) fleet_rome_support = Support(Nations.ITALY, self.territories.ROME, self.territories.APULIA, self.territories.VENICE) army_apulia_move = Move(Nations.ITALY, self.territories.APULIA, self.territories.VENICE) self.state.register(*pieces, army_austria_hold, fleet_rome_support, army_apulia_move) self.state.post_register_updates() process(self.state) self.assertEqual(fleet_rome_support.legal_decision, Outcomes.ILLEGAL) self.assertEqual(fleet_rome_support.illegal_message, illegal_messages.S002)
def test_ordering_a_unit_of_another_country(self): """ Check whether someone can not order a unit that does not belong to them. England has a fleet in London. Germany: F London - North Sea Order should fail. """ Fleet(self.state, 0, Nations.ENGLAND, self.territories.LONDON) order = Move(self.state, 0, Nations.GERMANY, self.territories.LONDON, self.territories.NORTH_SEA) Hold(self.state, 0, Nations.ENGLAND, self.territories.LONDON) process(self.state) self.assertTrue(order.illegal) self.assertEqual(order.illegal_code, '001') self.assertEqual( order.illegal_verbose, 'Cannot order a piece belonging to another nation.' )
def data_to_state(data): state = State() data = data # instantiate and register territories for territory_data in data['territories']: type = territory_data.pop('type') territory_class = terrtitory_type_dict[type] if not type == 'coastal': territory_data.pop('shared_coast_ids') if type == 'sea': territory_data.pop('supply_center', None) territory_data.pop('nationality', None) territory_data.pop('controlled_by', None) territory = territory_class(**territory_data) state.register(territory) # instantiate and register named coasts for named_coast_data in data['named_coasts']: t_id = named_coast_data.pop('territory_id') named_coast_data['parent'] = [ t for t in state.territories if t.id == t_id ][0] n_ids = named_coast_data.pop('neighbour_ids') named_coast_data['neighbours'] = [ t for t in state.territories if t.id in n_ids ] named_coast = NamedCoast(**named_coast_data) state.register(named_coast) # instantiate and register pieces for piece_data in data['pieces']: t_id = piece_data.pop('territory_id') n_id = piece_data.pop('named_coast_id', None) type = piece_data.pop('type') piece_data['territory'] = [ t for t in state.territories if t.id == t_id ][0] if type == 'fleet': named_coasts = [n for n in state.named_coasts if n.id == n_id] if named_coasts: piece_data['named_coast'] = named_coasts[0] piece_class = piece_type_dict[type] piece = piece_class(**piece_data) state.register(piece) # instantiate and register orders for order_data in data['orders']: type = order_data.pop('type') source_id = order_data.pop('source_id') order_data['source'] = [ t for t in state.territories if t.id == source_id ][0] if order_data.get('target_id'): target_id = order_data.pop('target_id') order_data['target'] = [ t for t in state.territories if t.id == target_id ][0] if order_data.get('target_coast_id'): target_coast_id = order_data.pop('target_coast_id') s = 'target_coast' if type == 'build': s = 'named_coast' order_data[s] = [ n for n in state.named_coasts if n.id == target_coast_id ][0] if order_data.get('aux_id'): aux_id = order_data.pop('aux_id') order_data['aux'] = [ t for t in state.territories if t.id == aux_id ][0] if not type == 'build': order_data.pop('piece_type') if not type == 'move': order_data.pop('via_convoy', None) order_class = order_type_dict[type] order = order_class(**order_data) state.register(order) # create an order for all pieces without orders for piece in state.pieces: if not piece.order: order_data = { '_id': 0, 'nation': piece.nation, 'source': piece.territory } hold = Hold(**order_data) state.register(hold) return state
def test_next_turn_piece_count_successful_hold(self): territory = InlandTerritory(self.state, 1, 'Paris', 2, []) Army(self.state, 1, self.nation.id, territory) Hold(self.state, 1, self.nation.id, territory) self.assertEqual(self.nation.next_turn_piece_count, 1)