def setUp(self): self.state = State() self.paris = InlandTerritory(1, 'Paris', 'France', [7]) self.london = CoastalTerritory(2, 'London', 'England', [3, 6], [3]) self.wales = CoastalTerritory(3, 'Wales', 'England', [2, 6], [2]) self.english_channel = SeaTerritory(6, 'English Channel', [2, 3]) self.brest = CoastalTerritory(7, 'Brest', 'France', [1, 6], []) self.rome = CoastalTerritory(8, 'Rome', 'Italy', [9], []) self.apulia = CoastalTerritory(9, 'Apulia', 'Italy', [8], []) self.spain = CoastalTerritory(10, 'Spain', None, [11, 12, 13, 14], [11, 12]) self.gascony = CoastalTerritory(11, 'Gascony', 'France', [10], []) self.marseilles = CoastalTerritory(12, 'Marseilles', 'France', [10], [10]) self.mid_atlantic = SeaTerritory(13, 'Mid Atlantic', [10]) self.gulf_of_lyon = SeaTerritory(14, 'Gulf of Lyon', [10]) self.spain_north_coast = NamedCoast(1, 'North Coast', self.spain, [self.gascony, self.mid_atlantic]) self.spain_south_coast = NamedCoast( 2, 'South Coast', self.spain, [self.marseilles, self.gulf_of_lyon, self.marseilles]) to_register = [ self.paris, self.london, self.wales, self.english_channel, self.brest, self.rome, self.apulia, self.spain, self.gascony, self.marseilles, self.mid_atlantic, self.gulf_of_lyon, self.spain_north_coast, self.spain_south_coast ] [self.state.register(o) for o in to_register]
def setUp(self): self.state = State() self.portugal = CoastalTerritory(11, 'Portugal', None, [], []) self.spain = CoastalTerritory(10, 'Spain', None, [], []) self.spain_south_coast = NamedCoast(2, 'South Coast', self.spain, []) to_register = [self.spain, self.spain_south_coast] [self.state.register(o) for o in to_register]
def test_order_exists(self): state = State() london = CoastalTerritory(1, 'London', 'England', [], []) wales = CoastalTerritory(2, 'Wales', 'England', [], []) army = Army('England', london) london_move = Move('England', london, wales) to_register = [london, wales, army, london_move] [state.register(o) for o in to_register] self.assertEqual(army.order, london_move)
def setUp(self): self.state = State() self.paris = InlandTerritory(1, 'Paris', 'France', [7]) self.london = CoastalTerritory(2, 'London', 'England', [3, 6], [3]) self.wales = CoastalTerritory(3, 'Wales', 'England', [2, 6], [2]) self.munich = InlandTerritory(4, 'Munich', 'Germany', [5]) self.silesia = InlandTerritory(5, 'Silesia', 'Germany', [4]) self.english_channel = SeaTerritory(6, 'English Channel', [2, 3]) self.brest = CoastalTerritory(7, 'Brest', 'France', [1, 6], []) to_register = [self.paris, self.london, self.wales, self.munich, self.silesia, self.english_channel, self.brest] [self.state.register(o) for o in to_register]
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_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_register_territory_updates_shared_coasts(self): state = State() rome = CoastalTerritory(1, 'rome', Nations.ITALY, [2, 4], [2, 3]) naples = CoastalTerritory(2, 'naples', Nations.ITALY, [1], [1]) tuscany = CoastalTerritory(3, 'tuscany', Nations.ITALY, [1, 4], [1]) venice = CoastalTerritory(4, 'venice', Nations.ITALY, [1, 3], []) state.register(rome) state.register(naples) state.register(tuscany) state.register(venice) self.assertTrue(all([t in rome.neighbours for t in [naples, venice]])) self.assertEqual({rome}, naples.neighbours) self.assertTrue(all([t in tuscany.neighbours for t in [rome, venice]])) self.assertTrue(all([t in venice.neighbours for t in [rome, tuscany]])) self.assertEqual(rome.shared_coasts, {naples, tuscany}) self.assertEqual(naples.shared_coasts, {rome}) self.assertEqual(tuscany.shared_coasts, {rome}) self.assertEqual(venice.shared_coasts, set())
def test_register_territory_updates_neighbours(self): state = State() paris = InlandTerritory(1, 'paris', Nations.FRANCE, [2, 3]) burgundy = InlandTerritory(2, 'burgundy', Nations.FRANCE, [1, 3]) gascony = InlandTerritory(3, 'gascony', Nations.FRANCE, [1, 2]) state.register(paris) state.register(burgundy) state.register(gascony) self.assertTrue(paris in burgundy.neighbours) self.assertTrue(paris in gascony.neighbours) self.assertTrue(burgundy in paris.neighbours) self.assertTrue(burgundy in gascony.neighbours) self.assertTrue(gascony in paris.neighbours) self.assertTrue(gascony in burgundy.neighbours)
def test_register_piece_updates_territory(self): state = State() paris = InlandTerritory(1, 'paris', Nations.FRANCE, []) army_paris = Army(0, Nations.FRANCE, paris) state.register(paris) state.register(army_paris) self.assertEqual(paris.piece, army_paris)
def test_register_named_coast_updates_territory(self): state = State() spain = CoastalTerritory(1, 'spain', None, [], []) spain_nc = NamedCoast(1, 'spain sc', spain, []) state.register(spain) state.register(spain_nc) self.assertTrue(spain_nc in spain.named_coasts)
class TestToDict(unittest.TestCase): def setUp(self): self.state = State() self.portugal = CoastalTerritory(11, 'Portugal', None, [], []) self.spain = CoastalTerritory(10, 'Spain', None, [], []) self.spain_south_coast = NamedCoast(2, 'South Coast', self.spain, []) to_register = [self.spain, self.spain_south_coast] [self.state.register(o) for o in to_register] def test_to_dict_dislodged(self): fleet = Fleet(1, 'England', self.spain, self.spain_south_coast) attacking_fleet = Fleet(2, 'France', self.portugal) fleet.dislodged_decision = Outcomes.DISLODGED fleet.dislodged_by = attacking_fleet fleet.attacker_territory = self.portugal self.assertEqual( fleet.to_dict(), { 'id': 1, 'dislodged_decision': 'dislodged', 'dislodged_by': 2, 'attacker_territory': 11, 'destroyed': False, 'destroyed_message': None, } ) def test_to_dict_not_dislodged(self): fleet = Fleet(1, 'England', self.spain, self.spain_south_coast) fleet.dislodged_decision = Outcomes.SUSTAINS self.assertEqual( fleet.to_dict(), { 'id': 1, 'dislodged_decision': 'sustains', 'dislodged_by': None, 'attacker_territory': None, 'destroyed': False, 'destroyed_message': None, } )
class TestCircularMovement(unittest.TestCase): def setUp(self): self.state = State() self.territories = Territories() self.named_coasts = NamedCoasts(self.territories) self.state = register_all(self.state, self.territories, self.named_coasts) def test_head_to_head(self): pieces = [ Fleet(Nations.TURKEY, self.territories.ANKARA), Army(Nations.TURKEY, self.territories.CONSTANTINOPLE), ] orders = [ Move(Nations.TURKEY, self.territories.ANKARA, self.territories.CONSTANTINOPLE), Move(Nations.TURKEY, self.territories.CONSTANTINOPLE, self.territories.ANKARA), ] self.state.register(*pieces, *orders) self.state.post_register_updates() result = find_circular_movements(orders) self.assertEqual(len(result), 0) def test_three_army_circular_movement(self): pieces = [ Fleet(Nations.TURKEY, self.territories.ANKARA), Army(Nations.TURKEY, self.territories.CONSTANTINOPLE), Army(Nations.TURKEY, self.territories.SMYRNA) ] orders = [ Move(Nations.TURKEY, self.territories.ANKARA, self.territories.CONSTANTINOPLE), Move(Nations.TURKEY, self.territories.CONSTANTINOPLE, self.territories.SMYRNA), Move(Nations.TURKEY, self.territories.SMYRNA, self.territories.ANKARA), ] self.state.register(*pieces, *orders) self.state.post_register_updates() result = find_circular_movements(orders) self.assertEqual(len(result), 1) self.assertTrue(all([o in result[0] for o in orders])) def test_four_army_circular_movement(self): pieces = [ Fleet(Nations.TURKEY, self.territories.ANKARA), Army(Nations.TURKEY, self.territories.CONSTANTINOPLE), Army(Nations.TURKEY, self.territories.SMYRNA), Army(Nations.TURKEY, self.territories.ARMENIA) ] orders = [ Move(Nations.TURKEY, self.territories.ANKARA, self.territories.CONSTANTINOPLE), Move(Nations.TURKEY, self.territories.CONSTANTINOPLE, self.territories.SMYRNA), Move(Nations.TURKEY, self.territories.SMYRNA, self.territories.ARMENIA), Move(Nations.TURKEY, self.territories.ARMENIA, self.territories.ANKARA), ] self.state.register(*pieces, *orders) self.state.post_register_updates() result = find_circular_movements(orders) self.assertEqual(len(result), 1) self.assertTrue(all([o in result[0] for o in orders])) def test_five_army_circular_movement(self): pieces = [ Fleet(Nations.TURKEY, self.territories.ANKARA), Army(Nations.TURKEY, self.territories.CONSTANTINOPLE), Army(Nations.TURKEY, self.territories.SMYRNA), Army(Nations.TURKEY, self.territories.ARMENIA), Army(Nations.TURKEY, self.territories.SYRIA), ] orders = [ Move(Nations.TURKEY, self.territories.ANKARA, self.territories.CONSTANTINOPLE), Move(Nations.TURKEY, self.territories.CONSTANTINOPLE, self.territories.SMYRNA), Move(Nations.TURKEY, self.territories.SMYRNA, self.territories.SYRIA), Move(Nations.TURKEY, self.territories.SYRIA, self.territories.ARMENIA), Move(Nations.TURKEY, self.territories.ARMENIA, self.territories.ANKARA), ] self.state.register(*pieces, *orders) self.state.post_register_updates() result = find_circular_movements(orders) self.assertEqual(len(result), 1) self.assertTrue(all([o in result[0] for o in orders])) def test_two_separate_circular_movements(self): pieces = [ Fleet(Nations.TURKEY, self.territories.ANKARA), Army(Nations.TURKEY, self.territories.CONSTANTINOPLE), Army(Nations.TURKEY, self.territories.SMYRNA), Army(Nations.FRANCE, self.territories.BREST), Army(Nations.FRANCE, self.territories.PICARDY), Army(Nations.FRANCE, self.territories.PARIS), ] orders = [ Move(Nations.TURKEY, self.territories.ANKARA, self.territories.CONSTANTINOPLE), Move(Nations.TURKEY, self.territories.CONSTANTINOPLE, self.territories.SMYRNA), Move(Nations.TURKEY, self.territories.SMYRNA, self.territories.ANKARA), Move(Nations.FRANCE, self.territories.BREST, self.territories.PICARDY), Move(Nations.FRANCE, self.territories.PICARDY, self.territories.PARIS), Move(Nations.FRANCE, self.territories.PARIS, self.territories.BREST), ] self.state.register(*pieces, *orders) self.state.post_register_updates() result = find_circular_movements(orders) self.assertEqual(len(result), 2) self.assertTrue(all([o in result[0] for o in orders[:3]])) self.assertTrue(all([o in result[1] for o in orders[3:]])) def test_empty_input(self): orders = [] result = find_circular_movements(orders) self.assertEqual(len(result), 0)
def setUp(self): self.state = State() self.territories = Territories() self.state = register_all(self.state, self.territories, [])
class TestCanReachFleet(unittest.TestCase): def setUp(self): self.state = State() self.paris = InlandTerritory(1, 'Paris', 'France', [7]) self.london = CoastalTerritory(2, 'London', 'England', [3, 6], [3]) self.wales = CoastalTerritory(3, 'Wales', 'England', [2, 6], [2]) self.english_channel = SeaTerritory(6, 'English Channel', [2, 3]) self.brest = CoastalTerritory(7, 'Brest', 'France', [1, 6], []) self.rome = CoastalTerritory(8, 'Rome', 'Italy', [9], []) self.apulia = CoastalTerritory(9, 'Apulia', 'Italy', [8], []) self.spain = CoastalTerritory(10, 'Spain', None, [11, 12, 13, 14], [11, 12]) self.gascony = CoastalTerritory(11, 'Gascony', 'France', [10], []) self.marseilles = CoastalTerritory(12, 'Marseilles', 'France', [10], [10]) self.mid_atlantic = SeaTerritory(13, 'Mid Atlantic', [10]) self.gulf_of_lyon = SeaTerritory(14, 'Gulf of Lyon', [10]) self.spain_north_coast = NamedCoast(1, 'North Coast', self.spain, [self.gascony, self.mid_atlantic]) self.spain_south_coast = NamedCoast( 2, 'South Coast', self.spain, [self.marseilles, self.gulf_of_lyon, self.marseilles]) to_register = [ self.paris, self.london, self.wales, self.english_channel, self.brest, self.rome, self.apulia, self.spain, self.gascony, self.marseilles, self.mid_atlantic, self.gulf_of_lyon, self.spain_north_coast, self.spain_south_coast ] [self.state.register(o) for o in to_register] def test_fleet_coastal_to_neighbouring_inland(self): fleet = Fleet('England', self.brest) self.state.register(fleet) self.assertFalse(fleet.can_reach(self.paris)) def test_fleet_coastal_to_neighbouring_coastal_shared_coast(self): fleet = Fleet('England', self.london) self.state.register(fleet) self.assertTrue(fleet.can_reach(self.wales)) def test_fleet_coastal_to_neighbouring_sea(self): fleet = Fleet('England', self.wales) self.state.register(fleet) self.assertTrue(fleet.can_reach(self.english_channel)) def test_fleet_coastal_to_non_neighbouring_coastal(self): fleet = Fleet('England', self.brest) self.state.register(fleet) self.assertFalse(fleet.can_reach(self.london)) def test_fleet_coastal_to_non_neighbouring_inland(self): fleet = Fleet('England', self.wales) self.state.register(fleet) self.assertFalse(fleet.can_reach(self.paris)) def test_fleet_coastal_to_neighbouring_coastal_unshared_coast(self): fleet = Fleet('England', self.rome) self.state.register(fleet) self.assertFalse(fleet.can_reach(self.apulia)) def test_named_coast_to_coastal(self): fleet = Fleet('England', self.spain, self.spain_north_coast) self.state.register(fleet) self.assertTrue(fleet.can_reach(self.gascony)) def test_named_coast_to_coastal_non_neighbouring(self): fleet = Fleet('England', self.spain, self.spain_north_coast) self.state.register(fleet) self.assertFalse(fleet.can_reach(self.marseilles)) def test_named_coast_to_sea(self): fleet = Fleet('England', self.spain, self.spain_north_coast) self.state.register(fleet) self.assertTrue(fleet.can_reach(self.mid_atlantic)) def test_named_coast_to_sea_non_neighbouring(self): fleet = Fleet('England', self.spain, self.spain_north_coast) self.state.register(fleet) self.assertFalse(fleet.can_reach(self.gulf_of_lyon)) def test_sea_to_named_coast(self): fleet = Fleet('England', self.gulf_of_lyon) self.state.register(fleet) self.assertTrue(fleet.can_reach(self.spain, self.spain_south_coast)) def test_sea_to_named_coast_non_neighbouring(self): fleet = Fleet('England', self.gulf_of_lyon) self.state.register(fleet) self.assertFalse(fleet.can_reach(self.spain, self.spain_north_coast)) def coastal_to_named_coast(self): fleet = Fleet('England', self.marseilles) self.state.register(fleet) self.assertTrue(fleet.can_reach(self.spain, self.spain_south_coast)) def coastal_to_named_coast_non_neighbouring(self): fleet = Fleet('England', self.marseilles) self.state.register(fleet) self.assertFalse(fleet.can_reach(self.spain, self.spain_north_coast)) def coastal_to_complex_no_named_coast(self): fleet = Fleet('England', self.marseilles) self.state.register(fleet) with self.assertRaises(ValueError): fleet.can_reach(self.spain)
class TestCanReachArmy(unittest.TestCase): def setUp(self): self.state = State() self.paris = InlandTerritory(1, 'Paris', 'France', [7]) self.london = CoastalTerritory(2, 'London', 'England', [3, 6], [3]) self.wales = CoastalTerritory(3, 'Wales', 'England', [2, 6], [2]) self.munich = InlandTerritory(4, 'Munich', 'Germany', [5]) self.silesia = InlandTerritory(5, 'Silesia', 'Germany', [4]) self.english_channel = SeaTerritory(6, 'English Channel', [2, 3]) self.brest = CoastalTerritory(7, 'Brest', 'France', [1, 6], []) to_register = [ self.paris, self.london, self.wales, self.munich, self.silesia, self.english_channel, self.brest ] [self.state.register(o) for o in to_register] def test_army_inland_to_neighbouring_inland(self): army = Army('England', self.munich) self.state.register(army) self.assertTrue(army.can_reach(self.silesia)) def test_army_inland_to_neighbouring_coastal(self): army = Army('England', self.paris) self.state.register(army) self.assertTrue(army.can_reach(self.brest)) def test_army_coastal_to_neighbouring_inland(self): army = Army('England', self.brest) self.state.register(army) self.assertTrue(army.can_reach(self.paris)) def test_army_coastal_to_neighbouring_coastal(self): army = Army('England', self.london) self.state.register(army) self.assertTrue(army.can_reach(self.wales)) def test_army_coastal_to_neighbouring_sea(self): army = Army('England', self.wales) self.state.register(army) self.assertFalse(army.can_reach(self.english_channel)) def test_army_coastal_to_non_neighbouring_coastal(self): army = Army('England', self.brest) self.state.register(army) self.assertTrue(army.can_reach(self.london)) def test_army_coastal_to_non_neighbouring_inland(self): army = Army('England', self.wales) self.state.register(army) self.assertFalse(army.can_reach(self.paris))
class TestHeadToHeadBattles(unittest.TestCase): def setUp(self): self.state = State() self.territories = Territories() self.named_coasts = NamedCoasts(self.territories) self.state = register_all(self.state, self.territories, self.named_coasts) def test_disloged_unit_has_not_effect_on_attackers_area(self): """ An army can follow. Germany: A Berlin - Prussia F Kiel - Berlin A Silesia Supports A Berlin - Prussia Russia: A Prussia - Berlin The fleet in Kiel will move to Berlin. """ pieces = [ Army(0, Nations.GERMANY, self.territories.BERLIN), Fleet(0, Nations.GERMANY, self.territories.KIEL), Army(0, Nations.GERMANY, self.territories.SILESIA), Army(0, Nations.RUSSIA, self.territories.PRUSSIA), ] orders = [ Move(0, Nations.GERMANY, self.territories.BERLIN, self.territories.PRUSSIA), Move(0, Nations.GERMANY, self.territories.KIEL, self.territories.BERLIN), Support(0, Nations.GERMANY, self.territories.SILESIA, self.territories.BERLIN, self.territories.PRUSSIA), Move(0, Nations.RUSSIA, self.territories.PRUSSIA, self.territories.BERLIN), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertTrue(orders[0].outcome, Outcomes.SUCCEEDS) self.assertTrue(orders[1].outcome, Outcomes.SUCCEEDS) self.assertTrue(orders[2].outcome, Outcomes.SUCCEEDS) def test_no_self_dislodgement_in_head_to_head_battle(self): """ Self dislodgement is not allowed. This also counts for head to head battles. Germany: A Berlin - Kiel F Kiel - Berlin A Munich Supports A Berlin - Kiel No unit will move. """ pieces = [ Army(0, Nations.GERMANY, self.territories.BERLIN), Fleet(0, Nations.GERMANY, self.territories.KIEL), Army(0, Nations.GERMANY, self.territories.MUNICH), ] orders = [ Move(0, Nations.GERMANY, self.territories.BERLIN, self.territories.KIEL), Move(0, Nations.GERMANY, self.territories.KIEL, self.territories.BERLIN), Support(0, Nations.GERMANY, self.territories.MUNICH, self.territories.BERLIN, self.territories.KIEL), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertTrue(orders[0].outcome, Outcomes.FAILS) self.assertTrue(orders[1].outcome, Outcomes.FAILS) self.assertTrue(orders[2].outcome, Outcomes.SUCCEEDS) def test_no_help_in_dislodging_own_unit(self): """ To help a foreign power to dislodge own unit in head to head battle is not possible. Germany: A Berlin - Kiel A Munich Supports F Kiel - Berlin England: F Kiel - Berlin No unit will move. """ pieces = [ Army(0, Nations.GERMANY, self.territories.BERLIN), Army(0, Nations.GERMANY, self.territories.MUNICH), Fleet(0, Nations.ENGLAND, self.territories.KIEL), ] orders = [ Move(0, Nations.GERMANY, self.territories.BERLIN, self.territories.KIEL), Support(0, Nations.GERMANY, self.territories.MUNICH, self.territories.KIEL, self.territories.BERLIN), Move(0, Nations.ENGLAND, self.territories.KIEL, self.territories.BERLIN), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertTrue(orders[0].outcome, Outcomes.FAILS) self.assertTrue(orders[1].outcome, Outcomes.SUCCEEDS) self.assertTrue(orders[2].outcome, Outcomes.FAILS) def test_non_dislodged_loser_has_still_effect(self): """ If in an unbalanced head to head battle the loser is not dislodged, it has still effect on the area of the attacker. Germany: F Holland - North Sea F Helgoland Bight Supports F Holland - North Sea F Skagerrak Supports F Holland - North Sea France: F North Sea - Holland F Belgium Supports F North Sea - Holland England: F Edinburgh Supports F Norwegian Sea - North Sea F Yorkshire Supports F Norwegian Sea - North Sea F Norwegian Sea - North Sea Austria: A Kiel Supports A Ruhr - Holland A Ruhr - Holland The French fleet in the North Sea is not dislodged due to the beleaguered garrison. Therefore, the Austrian army in Ruhr will not move to Holland. """ pieces = [ Fleet(0, Nations.GERMANY, self.territories.HOLLAND), Fleet(0, Nations.GERMANY, self.territories.HELGOLAND_BIGHT), Fleet(0, Nations.GERMANY, self.territories.SKAGERRAK), Fleet(0, Nations.FRANCE, self.territories.NORTH_SEA), Fleet(0, Nations.FRANCE, self.territories.BELGIUM), Fleet(0, Nations.ENGLAND, self.territories.EDINBURGH), Fleet(0, Nations.ENGLAND, self.territories.YORKSHIRE), Fleet(0, Nations.ENGLAND, self.territories.NORWEGIAN_SEA), Army(0, Nations.AUSTRIA, self.territories.KIEL), Army(0, Nations.AUSTRIA, self.territories.RUHR), ] orders = [ Move(0, Nations.GERMANY, self.territories.HOLLAND, self.territories.NORTH_SEA), Support(0, Nations.GERMANY, self.territories.HELGOLAND_BIGHT, self.territories.HOLLAND, self.territories.NORTH_SEA), Support(0, Nations.GERMANY, self.territories.SKAGERRAK, self.territories.HOLLAND, self.territories.NORTH_SEA), Move(0, Nations.FRANCE, self.territories.NORTH_SEA, self.territories.HOLLAND), Support(0, Nations.FRANCE, self.territories.BELGIUM, self.territories.NORTH_SEA, self.territories.HOLLAND), Support(0, Nations.ENGLAND, self.territories.EDINBURGH, self.territories.NORWEGIAN_SEA, self.territories.NORTH_SEA), Support(0, Nations.ENGLAND, self.territories.YORKSHIRE, self.territories.NORWEGIAN_SEA, self.territories.NORTH_SEA), Move(0, Nations.ENGLAND, self.territories.NORWEGIAN_SEA, self.territories.NORTH_SEA), Support(0, Nations.AUSTRIA, self.territories.KIEL, self.territories.RUHR, self.territories.HOLLAND), Move(0, Nations.AUSTRIA, self.territories.RUHR, self.territories.HOLLAND), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].outcome, Outcomes.FAILS) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) 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.SUCCEEDS) self.assertEqual(orders[6].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[7].outcome, Outcomes.FAILS) self.assertEqual(orders[8].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[9].outcome, Outcomes.FAILS) def test_loser_dislodged_by_another_army_still_has_effect(self): """ If in an unbalanced head to head battle the loser is dislodged by a unit not part of the head to head battle, the loser has still effect on the place of the winner of the head to head battle. Germany: F Holland - North Sea F Helgoland Bight Supports F Holland - North Sea F Skagerrak Supports F Holland - North Sea France: F North Sea - Holland F Belgium Supports F North Sea - Holland England: F Edinburgh Supports F Norwegian Sea - North Sea F Yorkshire Supports F Norwegian Sea - North Sea F Norwegian Sea - North Sea F London Supports F Norwegian Sea - North Sea Austria: A Kiel Supports A Ruhr - Holland A Ruhr - Holland The French fleet in the North Sea is dislodged but not by the German fleet in Holland. Therefore, the French fleet can still prevent that the Austrian army in Ruhr will move to Holland. So, the Austrian move in Ruhr fails and the German fleet in Holland is not dislodged. """ pieces = [ Fleet(0, Nations.GERMANY, self.territories.HOLLAND), Fleet(0, Nations.GERMANY, self.territories.HELGOLAND_BIGHT), Fleet(0, Nations.GERMANY, self.territories.SKAGERRAK), Fleet(0, Nations.FRANCE, self.territories.NORTH_SEA), Fleet(0, Nations.FRANCE, self.territories.BELGIUM), Fleet(0, Nations.ENGLAND, self.territories.EDINBURGH), Fleet(0, Nations.ENGLAND, self.territories.YORKSHIRE), Fleet(0, Nations.ENGLAND, self.territories.NORWEGIAN_SEA), Fleet(0, Nations.ENGLAND, self.territories.LONDON), Army(0, Nations.AUSTRIA, self.territories.KIEL), Army(0, Nations.AUSTRIA, self.territories.RUHR), ] orders = [ Move(0, Nations.GERMANY, self.territories.HOLLAND, self.territories.NORTH_SEA), Support(0, Nations.GERMANY, self.territories.HELGOLAND_BIGHT, self.territories.HOLLAND, self.territories.NORTH_SEA), Support(0, Nations.GERMANY, self.territories.SKAGERRAK, self.territories.HOLLAND, self.territories.NORTH_SEA), Move(0, Nations.FRANCE, self.territories.NORTH_SEA, self.territories.HOLLAND), Support(0, Nations.FRANCE, self.territories.BELGIUM, self.territories.NORTH_SEA, self.territories.HOLLAND), Support(0, Nations.ENGLAND, self.territories.EDINBURGH, self.territories.NORWEGIAN_SEA, self.territories.NORTH_SEA), Support(0, Nations.ENGLAND, self.territories.YORKSHIRE, self.territories.NORWEGIAN_SEA, self.territories.NORTH_SEA), Move(0, Nations.ENGLAND, self.territories.NORWEGIAN_SEA, self.territories.NORTH_SEA), Support(0, Nations.ENGLAND, self.territories.LONDON, self.territories.NORWEGIAN_SEA, self.territories.NORTH_SEA), Support(0, Nations.AUSTRIA, self.territories.KIEL, self.territories.RUHR, self.territories.HOLLAND), Move(0, Nations.AUSTRIA, self.territories.RUHR, self.territories.HOLLAND), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(pieces[0].dislodged_decision, Outcomes.SUSTAINS) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.SUCCEEDS) self.assertEqual(pieces[3].dislodged_decision, Outcomes.DISLODGED) self.assertEqual(pieces[3].dislodged_by, pieces[7]) self.assertEqual(orders[4].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[5].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[6].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[7].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[8].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[9].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[10].outcome, Outcomes.FAILS) 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_no_self_dislodgement_with_beleauguered_and_head_to_head(self): """ Similar to the previous test case, but now the beleaguered fleet is also engaged in a head to head battle. England: F North Sea - Norway 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 Again, none of the fleets move. """ 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 = [ Move(0, Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.NORWAY), 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(orders[0].outcome, Outcomes.FAILS) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) 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_almost_self_dislodgement_with_beleaguered_garrison(self): """ Similar to the previous test case, but now the beleaguered fleet is moving away. England: F North Sea - Norwegian Sea 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 Both the fleet in the North Sea and the fleet in Norway move. """ 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 = [ Move(0, Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.NORWEGIAN_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(orders[0].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) 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.SUCCEEDS) def test_almost_circular_movement_self_dislodgement_with_beleaguered_garrison( self): """ Similar to the previous test case, but now the beleaguered fleet is in circular movement with the weaker attacker. So, the circular movement fails. England: F North Sea - Denmark F Yorkshire Supports F Norway - North Sea Germany: F Holland Supports F Helgoland Bight - North Sea F Helgoland Bight - North Sea F Denmark - Helgoland Bight Russia: F Skagerrak Supports F Norway - North Sea F Norway - North Sea There is no movement of fleets. """ 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.GERMANY, self.territories.DENMARK), Fleet(0, Nations.RUSSIA, self.territories.SKAGERRAK), Fleet(0, Nations.RUSSIA, self.territories.NORWAY), ] orders = [ Move(0, Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.DENMARK), 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), Move(0, Nations.GERMANY, self.territories.DENMARK, self.territories.HELGOLAND_BIGHT), 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(orders[0].outcome, Outcomes.FAILS) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[3].outcome, Outcomes.FAILS) self.assertEqual(orders[4].outcome, Outcomes.FAILS) self.assertEqual(orders[5].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[6].outcome, Outcomes.FAILS) def test_almost_circular_movement_self_dislodgement_coasts(self): """ Similar to the previous test case, but now the beleaguered fleet is in a unit swap with the stronger attacker. So, the unit swap succeeds. To make the situation more complex, the swap is on an area with two coasts. France: A Spain - Portugal via Convoy F Mid-Atlantic Ocean Convoys A Spain - Portugal F Gulf of Lyon Supports F Portugal - Spain(nc) Germany: A Marseilles Supports A Gascony - Spain A Gascony - Spain Italy: F Portugal - Spain(nc) F Western Mediterranean Supports F Portugal - Spain(nc) The unit swap succeeds. Note that due to the success of the swap, there is no beleaguered garrison anymore. """ pieces = [ Army(0, Nations.FRANCE, self.territories.SPAIN), Fleet(0, Nations.FRANCE, self.territories.MID_ATLANTIC), Fleet(0, Nations.FRANCE, self.territories.GULF_OF_LYON), Army(0, Nations.GERMANY, self.territories.MARSEILLES), Army(0, Nations.GERMANY, self.territories.GASCONY), Fleet(0, Nations.ITALY, self.territories.PORTUGAL), Fleet(0, Nations.ITALY, self.territories.WESTERN_MEDITERRANEAN), ] orders = [ Move(0, Nations.FRANCE, self.territories.SPAIN, self.territories.PORTUGAL, via_convoy=True), Convoy(0, Nations.FRANCE, self.territories.MID_ATLANTIC, self.territories.SPAIN, self.territories.PORTUGAL), Support(0, Nations.FRANCE, self.territories.GULF_OF_LYON, self.territories.PORTUGAL, self.territories.SPAIN), Support(0, Nations.GERMANY, self.territories.MARSEILLES, self.territories.GASCONY, self.territories.SPAIN), Move(0, Nations.GERMANY, self.territories.GASCONY, self.territories.SPAIN), Move(0, Nations.ITALY, self.territories.PORTUGAL, self.territories.SPAIN, self.named_coasts.SPAIN_NC), Support(0, Nations.ITALY, self.territories.WESTERN_MEDITERRANEAN, self.territories.PORTUGAL, self.territories.SPAIN), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[3].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[4].outcome, Outcomes.FAILS) self.assertEqual(orders[5].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[6].outcome, Outcomes.SUCCEEDS) self.assertEqual(pieces[0].dislodged_decision, Outcomes.SUSTAINS) def test_support_on_attack_on_own_unit_can_be_used_for_other_means(self): """ A support on an attack on your own unit has still effect. It can prevent that another army will dislodge the unit. Austria: A Budapest - Rumania A Serbia Supports A Vienna - Budapest Italy: A Vienna - Budapest Russia: A Galicia - Budapest A Rumania Supports A Galicia - Budapest The support of Serbia on the Italian army prevents that the Russian army in Galicia will advance. No army will move. """ pieces = [ Army(0, Nations.AUSTRIA, self.territories.BUDAPEST), Army(0, Nations.AUSTRIA, self.territories.SERBIA), Army(0, Nations.ITALY, self.territories.VIENNA), Army(0, Nations.RUSSIA, self.territories.GALICIA), Army(0, Nations.RUSSIA, self.territories.RUMANIA), ] orders = [ Move(0, Nations.AUSTRIA, self.territories.BUDAPEST, self.territories.RUMANIA), Support(0, Nations.AUSTRIA, self.territories.SERBIA, self.territories.VIENNA, self.territories.BUDAPEST), Move(0, Nations.ITALY, self.territories.VIENNA, self.territories.BUDAPEST), Move(0, Nations.RUSSIA, self.territories.GALICIA, self.territories.BUDAPEST), Support(0, Nations.RUSSIA, self.territories.RUMANIA, self.territories.GALICIA, self.territories.BUDAPEST), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].outcome, Outcomes.FAILS) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.FAILS) self.assertEqual(orders[3].outcome, Outcomes.FAILS) self.assertEqual(orders[4].outcome, Outcomes.SUCCEEDS) 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(0, Nations.ENGLAND, self.territories.EDINBURGH), Fleet(0, Nations.ENGLAND, self.territories.YORKSHIRE), Fleet(0, Nations.FRANCE, self.territories.BELGIUM), Fleet(0, Nations.FRANCE, self.territories.ENGLISH_CHANNEL), Fleet(0, Nations.GERMANY, self.territories.NORTH_SEA), Fleet(0, Nations.RUSSIA, self.territories.NORWEGIAN_SEA), Fleet(0, Nations.RUSSIA, self.territories.NORWAY), ] orders = [ Support(0, Nations.ENGLAND, self.territories.EDINBURGH, self.territories.YORKSHIRE, self.territories.NORTH_SEA), Move(0, Nations.ENGLAND, self.territories.YORKSHIRE, self.territories.NORTH_SEA), Move(0, Nations.FRANCE, self.territories.BELGIUM, self.territories.NORTH_SEA), Support(0, Nations.FRANCE, self.territories.ENGLISH_CHANNEL, self.territories.BELGIUM, self.territories.NORTH_SEA), Hold(0, Nations.GERMANY, self.territories.NORTH_SEA), Move(0, Nations.RUSSIA, self.territories.NORWEGIAN_SEA, self.territories.NORTH_SEA), Support(0, Nations.RUSSIA, self.territories.NORWAY, self.territories.NORWEGIAN_SEA, self.territories.NORTH_SEA), ] self.state.register(*pieces, *orders) self.state.post_register_updates() 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_illegal_head_to_head_battle_can_still_defend(self): """ If in a head to head battle, one of the units makes an illegal move, than that unit has still the possibility to defend against attacks with strength of one. England: A Liverpool - Edinburgh Russia: F Edinburgh - Liverpool The move of the Russian fleet is illegal, but can still prevent the English army to enter Edinburgh. So, none of the units move. """ pieces = [ Army(0, Nations.ENGLAND, self.territories.LIVERPOOL), Fleet(0, Nations.RUSSIA, self.territories.EDINBURGH), ] orders = [ Move(0, Nations.ENGLAND, self.territories.LIVERPOOL, self.territories.EDINBURGH), Move(0, Nations.RUSSIA, self.territories.EDINBURGH, self.territories.LIVERPOOL), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertTrue(orders[1].illegal) self.assertEqual(orders[0].outcome, Outcomes.FAILS) self.assertEqual(orders[1].outcome, Outcomes.FAILS) def test_friendly_head_to_head_battle(self): """ In this case both units in the head to head battle prevent that the other one is dislodged. England: F Holland Supports A Ruhr - Kiel A Ruhr - Kiel France: A Kiel - Berlin A Munich Supports A Kiel - Berlin A Silesia Supports A Kiel - Berlin Germany: A Berlin - Kiel F Denmark Supports A Berlin - Kiel F Helgoland Bight Supports A Berlin - Kiel Russia: F Baltic Sea Supports A Prussia - Berlin A Prussia - Berlin None of the moves succeeds. This case is especially difficult for sequence based adjudicators. They will start adjudicating the head to head battle and continue to adjudicate the attack on one of the units part of the head to head battle. In this process, one of the sides of the head to head battle might be cancelled out. This happens in the DPTG. If this is adjudicated according to the DPTG, the unit in Ruhr or in Prussia will advance (depending on the order the units are adjudicated). This is clearly a bug in the DPTG. """ pieces = [ Fleet(0, Nations.ENGLAND, self.territories.HOLLAND), Army(0, Nations.ENGLAND, self.territories.RUHR), Army(0, Nations.FRANCE, self.territories.KIEL), Army(0, Nations.FRANCE, self.territories.MUNICH), Army(0, Nations.FRANCE, self.territories.SILESIA), Army(0, Nations.GERMANY, self.territories.BERLIN), Fleet(0, Nations.GERMANY, self.territories.DENMARK), Fleet(0, Nations.GERMANY, self.territories.HELGOLAND_BIGHT), Fleet(0, Nations.RUSSIA, self.territories.BALTIC_SEA), Army(0, Nations.RUSSIA, self.territories.PRUSSIA), ] orders = [ Support(0, Nations.ENGLAND, self.territories.HOLLAND, self.territories.RUHR, self.territories.KIEL), Move(0, Nations.ENGLAND, self.territories.RUHR, self.territories.KIEL), Move(0, Nations.FRANCE, self.territories.KIEL, self.territories.BERLIN), Support(0, Nations.FRANCE, self.territories.MUNICH, self.territories.KIEL, self.territories.BERLIN), Support(0, Nations.FRANCE, self.territories.SILESIA, self.territories.KIEL, self.territories.BERLIN), Move(0, Nations.GERMANY, self.territories.BERLIN, self.territories.KIEL), Support(0, Nations.GERMANY, self.territories.DENMARK, self.territories.BERLIN, self.territories.KIEL), Support(0, Nations.GERMANY, self.territories.HELGOLAND_BIGHT, self.territories.BERLIN, self.territories.KIEL), Support(0, Nations.RUSSIA, self.territories.BALTIC_SEA, self.territories.PRUSSIA, self.territories.BERLIN), Move(0, Nations.RUSSIA, self.territories.PRUSSIA, self.territories.BERLIN), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[1].outcome, Outcomes.FAILS) self.assertEqual(orders[2].outcome, Outcomes.FAILS) self.assertEqual(orders[5].outcome, Outcomes.FAILS) self.assertEqual(orders[9].outcome, Outcomes.FAILS)
class TestConvoys(unittest.TestCase): def setUp(self): self.state = State() self.territories = Territories() self.named_coasts = NamedCoasts(self.territories) self.state = register_all(self.state, self.territories, self.named_coasts) def test_no_convoy_in_coastal_area(self): """ A fleet in a coastal area may not convoy. Turkey: A Greece - Sevastopol F Aegean Sea Convoys A Greece - Sevastopol F Constantinople Convoys A Greece - Sevastopol F Black Sea Convoys A Greece - Sevastopol The convoy in Constantinople is not possible. So, the army in Greece will not move to Sevastopol. """ pieces = [ Army(Nations.TURKEY, self.territories.GREECE), Fleet(Nations.TURKEY, self.territories.AEGEAN_SEA), Fleet(Nations.TURKEY, self.territories.CONSTANTINOPLE), Fleet(Nations.TURKEY, self.territories.BLACK_SEA), ] orders = [ Move(Nations.TURKEY, self.territories.GREECE, self.territories.SEVASTAPOL, via_convoy=True), Convoy(Nations.TURKEY, self.territories.AEGEAN_SEA, self.territories.GREECE, self.territories.SEVASTAPOL), Convoy(Nations.TURKEY, self.territories.CONSTANTINOPLE, self.territories.GREECE, self.territories.SEVASTAPOL), Convoy(Nations.TURKEY, self.territories.BLACK_SEA, self.territories.GREECE, self.territories.SEVASTAPOL), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].move_decision, Outcomes.FAILS) self.assertEqual(orders[0].legal_decision, Outcomes.LEGAL) self.assertEqual(orders[2].legal_decision, Outcomes.ILLEGAL) def test_army_being_convoyed_can_bounce_as_normal(self): """ Armies being convoyed bounce on other units just as armies that are not being convoyed. England: F English Channel Convoys A London - Brest A London - Brest France: A Paris - Brest The English army in London bounces on the French army in Paris. Both units do not move. """ pieces = [ Fleet(Nations.ENGLAND, self.territories.ENGLISH_CHANNEL), Army(Nations.ENGLAND, self.territories.LONDON), Army(Nations.FRANCE, self.territories.PARIS), ] orders = [ Convoy(Nations.ENGLAND, self.territories.ENGLISH_CHANNEL, self.territories.LONDON, self.territories.BREST), Move(Nations.ENGLAND, self.territories.LONDON, self.territories.BREST, via_convoy=True), Move(Nations.FRANCE, self.territories.PARIS, self.territories.BREST), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[1].path_decision(), Outcomes.PATH) self.assertEqual(orders[1].move_decision, Outcomes.FAILS) self.assertEqual(orders[2].move_decision, Outcomes.FAILS) def test_army_being_convoyed_can_receive_support(self): """ Armies being convoyed can receive support as in any other move. England: F English Channel Convoys A London - Brest A London - Brest F Mid-Atlantic Ocean Supports A London - Brest France: A Paris - Brest The army in London receives support and beats the army in Paris. This means that the army London will end in Brest and the French army in Paris stays in Paris. """ pieces = [ Fleet(Nations.ENGLAND, self.territories.ENGLISH_CHANNEL), Army(Nations.ENGLAND, self.territories.LONDON), Fleet(Nations.ENGLAND, self.territories.MID_ATLANTIC), Army(Nations.FRANCE, self.territories.PARIS), ] orders = [ Convoy(Nations.ENGLAND, self.territories.ENGLISH_CHANNEL, self.territories.LONDON, self.territories.BREST), Move(Nations.ENGLAND, self.territories.LONDON, self.territories.BREST, via_convoy=True), Support(Nations.ENGLAND, self.territories.MID_ATLANTIC, self.territories.LONDON, self.territories.BREST), Move(Nations.FRANCE, self.territories.PARIS, self.territories.BREST), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[1].path_decision(), Outcomes.PATH) self.assertEqual(orders[1].move_decision, Outcomes.MOVES) self.assertEqual(orders[2].support_decision, Outcomes.GIVEN) self.assertEqual(orders[3].move_decision, Outcomes.FAILS) def test_attacked_convoy_is_not_disrupted(self): """ A convoy can only be disrupted by dislodging the fleets. Attacking is not sufficient. England: F North Sea Convoys A London - Holland A London - Holland Germany: F Skagerrak - North Sea The army in London will successfully convoy and end in Holland. """ pieces = [ Fleet(Nations.ENGLAND, self.territories.NORTH_SEA), Army(Nations.ENGLAND, self.territories.LONDON), Fleet(Nations.GERMANY, self.territories.SKAGERRAK) ] orders = [ Convoy(Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.LONDON, self.territories.HOLLAND), Move(Nations.ENGLAND, self.territories.LONDON, self.territories.HOLLAND, via_convoy=True), Move(Nations.GERMANY, self.territories.SKAGERRAK, self.territories.NORTH_SEA), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[1].path_decision(), Outcomes.PATH) self.assertEqual(orders[1].move_decision, Outcomes.MOVES) self.assertEqual(orders[2].move_decision, Outcomes.FAILS) def test_beleaguered_convoy_is_not_disrupted(self): """ Even when a convoy is in a beleaguered garrison it is not disrupted. England: F North Sea Convoys A London - Holland A London - Holland France: F English Channel - North Sea F Belgium Supports F English Channel - North Sea Germany: F Skagerrak - North Sea F Denmark Supports F Skagerrak - North Sea The army in London will successfully convoy and end in Holland. """ pieces = [ Fleet(Nations.ENGLAND, self.territories.NORTH_SEA), Army(Nations.ENGLAND, self.territories.LONDON), Fleet(Nations.FRANCE, self.territories.ENGLISH_CHANNEL), Fleet(Nations.FRANCE, self.territories.BELGIUM), Fleet(Nations.GERMANY, self.territories.SKAGERRAK), Fleet(Nations.GERMANY, self.territories.DENMARK) ] orders = [ Convoy(Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.LONDON, self.territories.HOLLAND), Move(Nations.ENGLAND, self.territories.LONDON, self.territories.HOLLAND, via_convoy=True), Move(Nations.FRANCE, self.territories.ENGLISH_CHANNEL, self.territories.NORTH_SEA), Support(Nations.FRANCE, self.territories.BELGIUM, self.territories.ENGLISH_CHANNEL, self.territories.NORTH_SEA), Move(Nations.GERMANY, self.territories.SKAGERRAK, self.territories.NORTH_SEA), Support(Nations.GERMANY, self.territories.DENMARK, self.territories.SKAGERRAK, self.territories.NORTH_SEA), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[1].path_decision(), Outcomes.PATH) self.assertEqual(orders[1].move_decision, Outcomes.MOVES) self.assertEqual(orders[2].move_decision, Outcomes.FAILS) self.assertEqual(orders[3].support_decision, Outcomes.GIVEN) self.assertEqual(orders[4].move_decision, Outcomes.FAILS) self.assertEqual(orders[5].support_decision, Outcomes.GIVEN) def test_dislodged_convoy_does_not_cut_support(self): """ When a fleet of a convoy is dislodged, the convoy is completely cancelled. So, no support is cut. England: F North Sea Convoys A London - Holland A London - Holland Germany: A Holland Supports A Belgium A Belgium Supports A Holland F Helgoland Bight Supports F Skagerrak - North Sea F Skagerrak - North Sea France: A Picardy - Belgium A Burgundy Supports A Picardy - Belgium The hold order of Holland on Belgium will sustain and Belgium will not be dislodged by the French in Picardy. """ pieces = [ Fleet(Nations.ENGLAND, self.territories.NORTH_SEA), Army(Nations.ENGLAND, self.territories.LONDON), Army(Nations.GERMANY, self.territories.HOLLAND), Army(Nations.GERMANY, self.territories.BELGIUM), Fleet(Nations.GERMANY, self.territories.HELGOLAND_BIGHT), Fleet(Nations.GERMANY, self.territories.SKAGERRAK), Army(Nations.FRANCE, self.territories.PICARDY), Army(Nations.FRANCE, self.territories.BURGUNDY), ] orders = [ Convoy(Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.LONDON, self.territories.HOLLAND), Move(Nations.ENGLAND, self.territories.LONDON, self.territories.HOLLAND, via_convoy=True), Support(Nations.GERMANY, self.territories.HOLLAND, self.territories.BELGIUM, self.territories.BELGIUM), Support(Nations.GERMANY, self.territories.BELGIUM, self.territories.HOLLAND, self.territories.HOLLAND), Support(Nations.GERMANY, self.territories.HELGOLAND_BIGHT, self.territories.SKAGERRAK, self.territories.NORTH_SEA), Move(Nations.GERMANY, self.territories.SKAGERRAK, self.territories.NORTH_SEA), Move(Nations.FRANCE, self.territories.PICARDY, self.territories.BELGIUM), Support(Nations.FRANCE, self.territories.BURGUNDY, self.territories.PICARDY, self.territories.BELGIUM), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(pieces[0].dislodged_decision, Outcomes.DISLODGED) self.assertEqual(orders[1].path_decision(), Outcomes.NO_PATH) self.assertEqual(orders[1].move_decision, Outcomes.FAILS) self.assertEqual(orders[2].support_decision, Outcomes.GIVEN) self.assertEqual(orders[3].support_decision, Outcomes.CUT) self.assertEqual(pieces[3].dislodged_decision, Outcomes.SUSTAINS) self.assertEqual(orders[4].support_decision, Outcomes.GIVEN) self.assertEqual(orders[5].move_decision, Outcomes.MOVES) self.assertEqual(orders[6].move_decision, Outcomes.FAILS) self.assertEqual(orders[7].support_decision, Outcomes.GIVEN) @unittest.skip( 'test_dislodged_convoy_does_not_cause_contested_area - involves retreat' ) def test_dislodged_convoy_does_not_cause_contested_area(self): """ When a fleet of a convoy is dislodged, the landing area is not contested, so other units can retreat to that area. England: F North Sea Convoys A London - Holland A London - Holland Germany: F Helgoland Bight Supports F Skagerrak - North Sea F Skagerrak - North Sea The dislodged English fleet can retreat to Holland. """ pieces = [ Fleet(Nations.ENGLAND, self.territories.NORTH_SEA), Army(Nations.ENGLAND, self.territories.LONDON), Fleet(Nations.GERMANY, self.territories.HELGOLAND_BIGHT), Fleet(Nations.GERMANY, self.territories.SKAGERRAK), ] orders = [ Convoy(Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.LONDON, self.territories.HOLLAND), Move(Nations.ENGLAND, self.territories.LONDON, self.territories.HOLLAND, via_convoy=True), Support(Nations.GERMANY, self.territories.HELGOLAND_BIGHT, self.territories.SKAGERRAK, self.territories.NORTH_SEA), Move(Nations.GERMANY, self.territories.SKAGERRAK, self.territories.NORTH_SEA), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(pieces[0].dislodged_decision, Outcomes.DISLODGED) self.assertEqual(orders[1].path_decision(), Outcomes.NO_PATH) self.assertEqual(orders[1].move_decision, Outcomes.FAILS) self.assertEqual(orders[2].support_decision, Outcomes.GIVEN) self.assertEqual(orders[3].move_decision, Outcomes.MOVES) def test_dislodged_convoy_does_not_cause_a_bounce(self): """ When a fleet of a convoy is dislodged, then there will be no bounce in the landing area. England: F North Sea Convoys A London - Holland A London - Holland Germany: F Helgoland Bight Supports F Skagerrak - North Sea F Skagerrak - North Sea A Belgium - Holland The army in Belgium will not bounce and move to Holland. """ pieces = [ Fleet(Nations.ENGLAND, self.territories.NORTH_SEA), Army(Nations.ENGLAND, self.territories.LONDON), Fleet(Nations.GERMANY, self.territories.HELGOLAND_BIGHT), Fleet(Nations.GERMANY, self.territories.SKAGERRAK), Army(Nations.GERMANY, self.territories.BELGIUM), ] orders = [ Convoy(Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.LONDON, self.territories.HOLLAND), Move(Nations.ENGLAND, self.territories.LONDON, self.territories.HOLLAND, via_convoy=True), Support(Nations.GERMANY, self.territories.HELGOLAND_BIGHT, self.territories.SKAGERRAK, self.territories.NORTH_SEA), Move(Nations.GERMANY, self.territories.SKAGERRAK, self.territories.NORTH_SEA), Move(Nations.GERMANY, self.territories.BELGIUM, self.territories.HOLLAND), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(pieces[0].dislodged_decision, Outcomes.DISLODGED) self.assertEqual(orders[1].path_decision(), Outcomes.NO_PATH) self.assertEqual(orders[1].move_decision, Outcomes.FAILS) self.assertEqual(orders[2].support_decision, Outcomes.GIVEN) self.assertEqual(orders[3].move_decision, Outcomes.MOVES) self.assertEqual(orders[4].move_decision, Outcomes.MOVES) def test_dislodged_multi_route_convoy(self): """ When a fleet of a convoy with multiple routes is dislodged, the result depends on the rulebook that is used. England: F English Channel Convoys A London - Belgium F North Sea Convoys A London - Belgium A London - Belgium France: F Brest Supports F Mid-Atlantic Ocean - English Channel F Mid-Atlantic Ocean - English Channel The French fleet in Mid Atlantic Ocean will dislodge the convoying fleet in the English Channel. If the 1971 rules are used (see issue 4.A.1), this will disrupt the convoy and the army will stay in London. When the 1982 or 2000 rulebook is used (which I prefer) the army can still go via the North Sea and the convoy succeeds and the London army will end in Belgium. """ pieces = [ Fleet(Nations.ENGLAND, self.territories.ENGLISH_CHANNEL), Fleet(Nations.ENGLAND, self.territories.NORTH_SEA), Army(Nations.ENGLAND, self.territories.LONDON), Fleet(Nations.FRANCE, self.territories.BREST), Fleet(Nations.FRANCE, self.territories.MID_ATLANTIC), ] orders = [ Convoy(Nations.ENGLAND, self.territories.ENGLISH_CHANNEL, self.territories.LONDON, self.territories.BELGIUM), Convoy(Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.LONDON, self.territories.BELGIUM), Move(Nations.ENGLAND, self.territories.LONDON, self.territories.BELGIUM, via_convoy=True), Support(Nations.FRANCE, self.territories.BREST, self.territories.MID_ATLANTIC, self.territories.ENGLISH_CHANNEL), Move(Nations.FRANCE, self.territories.MID_ATLANTIC, self.territories.ENGLISH_CHANNEL), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(pieces[0].dislodged_decision, Outcomes.DISLODGED) self.assertEqual(pieces[1].dislodged_decision, Outcomes.SUSTAINS) self.assertEqual(orders[2].path_decision(), Outcomes.PATH) self.assertEqual(orders[2].move_decision, Outcomes.MOVES) self.assertEqual(orders[3].support_decision, Outcomes.GIVEN) self.assertEqual(orders[4].move_decision, Outcomes.MOVES) def test_dislodge_of_multi_route_convoy_with_foreign_fleet(self): """ When the 1971 rulebook is used "unwanted" multi-route convoys are possible. England: F North Sea Convoys A London - Belgium A London - Belgium Germany: F English Channel Convoys A London - Belgium France: F Brest Supports F Mid-Atlantic Ocean - English Channel F Mid-Atlantic Ocean - English Channel If the 1982 or 2000 rulebook is used (which I prefer), it makes no difference that the convoying fleet in the English Channel is German. It will take the convoy via the North Sea anyway and the army in London will end in Belgium. However, when the 1971 rules are used, the German convoy is "unwanted". According to the DPTG the German fleet should be ignored in the English convoy, since there is a convoy path with only English fleets. That means that the convoy is not disrupted and the English army in London will end in Belgium. See also issue 4.A.1. """ pieces = [ Fleet(Nations.ENGLAND, self.territories.NORTH_SEA), Army(Nations.ENGLAND, self.territories.LONDON), Fleet(Nations.GERMANY, self.territories.ENGLISH_CHANNEL), Fleet(Nations.FRANCE, self.territories.BREST), Fleet(Nations.FRANCE, self.territories.MID_ATLANTIC), ] orders = [ Convoy(Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.LONDON, self.territories.BELGIUM), Move(Nations.ENGLAND, self.territories.LONDON, self.territories.BELGIUM, via_convoy=True), Convoy(Nations.GERMANY, self.territories.ENGLISH_CHANNEL, self.territories.LONDON, self.territories.BELGIUM), Support(Nations.FRANCE, self.territories.BREST, self.territories.MID_ATLANTIC, self.territories.ENGLISH_CHANNEL), Move(Nations.FRANCE, self.territories.MID_ATLANTIC, self.territories.ENGLISH_CHANNEL), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(pieces[0].dislodged_decision, Outcomes.SUSTAINS) self.assertEqual(orders[1].path_decision(), Outcomes.PATH) self.assertEqual(orders[1].move_decision, Outcomes.MOVES) self.assertEqual(pieces[2].dislodged_decision, Outcomes.DISLODGED) self.assertEqual(orders[3].support_decision, Outcomes.GIVEN) self.assertEqual(orders[4].move_decision, Outcomes.MOVES) def test_dislodge_of_multi_route_convoy_with_only_foreign_fleets(self): """ When the 1971 rulebook is used, "unwanted" convoys can not be ignored in all cases. England: A London - Belgium Germany: F English Channel Convoys A London - Belgium Russia: F North Sea Convoys A London - Belgium France: F Brest Supports F Mid-Atlantic Ocean - English Channel F Mid-Atlantic Ocean - English Channel If the 1982 or 2000 rulebook is used (which I prefer), it makes no difference that the convoying fleets are not English. It will take the convoy via the North Sea anyway and the army in London will end in Belgium. However, when the 1971 rules are used, the situation is different. Since both the fleet in the English Channel as the fleet in North Sea are not English, it can not be concluded that the German fleet is "unwanted". Therefore, one of the routes of the convoy is disrupted and that means that the complete convoy is disrupted. The army in London will stay in London. See also issue 4.A.1. """ pieces = [ Army(Nations.ENGLAND, self.territories.LONDON), Fleet(Nations.GERMANY, self.territories.ENGLISH_CHANNEL), Fleet(Nations.RUSSIA, self.territories.NORTH_SEA), Fleet(Nations.FRANCE, self.territories.BREST), Fleet(Nations.FRANCE, self.territories.MID_ATLANTIC), ] orders = [ Move(Nations.ENGLAND, self.territories.LONDON, self.territories.BELGIUM, via_convoy=True), Convoy(Nations.GERMANY, self.territories.ENGLISH_CHANNEL, self.territories.LONDON, self.territories.BELGIUM), Convoy(Nations.RUSSIA, self.territories.NORTH_SEA, self.territories.LONDON, self.territories.BELGIUM), Support(Nations.FRANCE, self.territories.BREST, self.territories.MID_ATLANTIC, self.territories.ENGLISH_CHANNEL), Move(Nations.FRANCE, self.territories.MID_ATLANTIC, self.territories.ENGLISH_CHANNEL), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].path_decision(), Outcomes.PATH) self.assertEqual(orders[0].move_decision, Outcomes.MOVES) self.assertEqual(pieces[1].dislodged_decision, Outcomes.DISLODGED) self.assertEqual(pieces[2].dislodged_decision, Outcomes.SUSTAINS) self.assertEqual(orders[3].support_decision, Outcomes.GIVEN) self.assertEqual(orders[4].move_decision, Outcomes.MOVES) def test_dislodge_convoying_fleet_not_on_route(self): """ When the rule is used that convoys are disrupted when one of the routes is disrupted (see issue 4.A.1), the convoy is not necessarily disrupted when one of the fleets ordered to convoy is dislodged. England: F English Channel Convoys A London - Belgium A London - Belgium F Irish Sea Convoys A London - Belgium France: F North Atlantic Ocean Supports F Mid-Atlantic Ocean - Irish Sea F Mid-Atlantic Ocean - Irish Sea Even when convoys are disrupted when one of the routes is disrupted (see issue 4.A.1), the convoy from London to Belgium will still succeed, since the dislodged fleet in the Irish Sea is not part of any route, although it can be reached from the starting point London. """ pieces = [ Fleet(Nations.ENGLAND, self.territories.ENGLISH_CHANNEL), Army(Nations.ENGLAND, self.territories.LONDON), Fleet(Nations.ENGLAND, self.territories.IRISH_SEA), Fleet(Nations.FRANCE, self.territories.NORTH_ATLANTIC), Fleet(Nations.FRANCE, self.territories.MID_ATLANTIC), ] orders = [ Convoy(Nations.ENGLAND, self.territories.ENGLISH_CHANNEL, self.territories.LONDON, self.territories.BELGIUM), Move(Nations.ENGLAND, self.territories.LONDON, self.territories.BELGIUM, via_convoy=True), Convoy(Nations.ENGLAND, self.territories.IRISH_SEA, self.territories.LONDON, self.territories.BELGIUM), Support(Nations.FRANCE, self.territories.NORTH_ATLANTIC, self.territories.MID_ATLANTIC, self.territories.IRISH_SEA), Move(Nations.FRANCE, self.territories.MID_ATLANTIC, self.territories.IRISH_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[1].path_decision(), Outcomes.PATH) self.assertEqual(orders[1].move_decision, Outcomes.MOVES) self.assertEqual(pieces[2].dislodged_decision, Outcomes.DISLODGED) self.assertEqual(orders[3].support_decision, Outcomes.GIVEN) self.assertEqual(orders[4].move_decision, Outcomes.MOVES) @unittest.skip('test_simple_convoy_paradox - Convoy paradox') def test_simple_convoy_paradox(self): """ The most common paradox is when the attacked unit supports an attack on one of the convoying fleets. England: F London Supports F Wales - English Channel F Wales - English Channel France: A Brest - London F English Channel Convoys A Brest - London This situation depends on how paradoxes are handled (see issue (4.A.2). In case of the 'All Hold' rule (fully applied, not just as "backup" rule), both the movement of the English fleet in Wales as the France convoy in Brest are part of the paradox and fail. In all other rules of paradoxical convoys (including the Szykman rule which I prefer), the support of London is not cut. That means that the fleet in the English Channel is dislodged. """ pieces = [ Fleet(Nations.ENGLAND, self.territories.LONDON), Fleet(Nations.ENGLAND, self.territories.WALES), Army(Nations.FRANCE, self.territories.BREST), Fleet(Nations.FRANCE, self.territories.ENGLISH_CHANNEL), ] orders = [ Support(Nations.ENGLAND, self.territories.LONDON, self.territories.WALES, self.territories.ENGLISH_CHANNEL), Move(Nations.ENGLAND, self.territories.WALES, self.territories.ENGLISH_CHANNEL), Move(Nations.FRANCE, self.territories.BREST, self.territories.LONDON), Convoy(Nations.FRANCE, self.territories.ENGLISH_CHANNEL, self.territories.BREST, self.territories.LONDON), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].support_decision, Outcomes.GIVEN) self.assertEqual(orders[1].move_decision, Outcomes.MOVES) self.assertEqual(orders[2].path_decision, Outcomes.NO_PATH) self.assertEqual(orders[2].move_decision, Outcomes.FAILS) self.assertEqual(pieces[3].dislodged_decision, Outcomes.DISLODGED)
class TestCircularMovement(unittest.TestCase): def setUp(self): self.state = State() self.territories = Territories() self.named_coasts = NamedCoasts(self.territories) self.state = register_all(self.state, self.territories, self.named_coasts) def test_three_army_circular_movement(self): """ Three units can change place, even in spring 1901. Turkey: F Ankara - Constantinople A Constantinople - Smyrna A Smyrna - Ankara All three units will move. """ pieces = [ Fleet(0, Nations.TURKEY, self.territories.ANKARA), Army(0, Nations.TURKEY, self.territories.CONSTANTINOPLE), Army(0, Nations.TURKEY, self.territories.SMYRNA) ] orders = [ Move(0, Nations.TURKEY, self.territories.ANKARA, self.territories.CONSTANTINOPLE), Move(0, Nations.TURKEY, self.territories.CONSTANTINOPLE, self.territories.SMYRNA), Move(0, Nations.TURKEY, self.territories.SMYRNA, self.territories.ANKARA), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.SUCCEEDS) def test_three_army_circular_movement_with_support(self): """ Three units can change place, even when one gets support. Turkey: F Ankara - Constantinople A Constantinople - Smyrna A Smyrna - Ankara A Bulgaria Supports F Ankara - Constantinople Of course the three units will move, but knowing how programs are written, this can confuse the adjudicator. """ pieces = [ Fleet(0, Nations.TURKEY, self.territories.ANKARA), Army(0, Nations.TURKEY, self.territories.BULGARIA), Army(0, Nations.TURKEY, self.territories.CONSTANTINOPLE), Army(0, Nations.TURKEY, self.territories.SMYRNA) ] orders = [ Move(0, Nations.TURKEY, self.territories.ANKARA, self.territories.CONSTANTINOPLE), Move(0, Nations.TURKEY, self.territories.CONSTANTINOPLE, self.territories.SMYRNA), Move(0, Nations.TURKEY, self.territories.SMYRNA, self.territories.ANKARA), Support(0, Nations.TURKEY, self.territories.BULGARIA, self.territories.ANKARA, self.territories.CONSTANTINOPLE), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[3].outcome, Outcomes.SUCCEEDS) def test_disrupted_three_army_circular_movement(self): """ When one of the units bounces, the whole circular movement will hold. Turkey: F Ankara - Constantinople A Constantinople - Smyrna A Smyrna - Ankara A Bulgaria - Constantinople Every unit will keep its place. """ pieces = [ Fleet(0, Nations.TURKEY, self.territories.ANKARA), Army(0, Nations.TURKEY, self.territories.BULGARIA), Army(0, Nations.TURKEY, self.territories.CONSTANTINOPLE), Army(0, Nations.TURKEY, self.territories.SMYRNA) ] orders = [ Move(0, Nations.TURKEY, self.territories.ANKARA, self.territories.CONSTANTINOPLE), Move(0, Nations.TURKEY, self.territories.CONSTANTINOPLE, self.territories.SMYRNA), Move(0, Nations.TURKEY, self.territories.SMYRNA, self.territories.ANKARA), Move(0, Nations.TURKEY, self.territories.BULGARIA, self.territories.CONSTANTINOPLE), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].outcome, Outcomes.FAILS) self.assertEqual(orders[1].outcome, Outcomes.FAILS) self.assertEqual(orders[2].outcome, Outcomes.FAILS) self.assertEqual(orders[3].outcome, Outcomes.FAILS) def test_circular_movement_with_attacked_convoy(self): """ When the circular movement contains an attacked convoy, the circular movement succeeds. The adjudication algorithm should handle attack of convoys before calculating circular movement. Austria: A Trieste - Serbia A Serbia - Bulgaria Turkey: A Bulgaria - Trieste F Aegean Sea Convoys A Bulgaria - Trieste F Ionian Sea Convoys A Bulgaria - Trieste F Adriatic Sea Convoys A Bulgaria - Trieste Italy: F Naples - Ionian Sea The fleet in the Ionian Sea is attacked but not dislodged. The circular movement succeeds. The Austrian and Turkish armies will advance. """ pieces = [ Army(0, Nations.AUSTRIA, self.territories.TRIESTE), Army(0, Nations.AUSTRIA, self.territories.SERBIA), Army(0, Nations.TURKEY, self.territories.BULGARIA), Fleet(0, Nations.TURKEY, self.territories.AEGEAN_SEA), Fleet(0, Nations.TURKEY, self.territories.IONIAN_SEA), Fleet(0, Nations.TURKEY, self.territories.ADRIATIC_SEA), Fleet(0, Nations.ITALY, self.territories.NAPLES), ] orders = [ Move(0, Nations.AUSTRIA, self.territories.TRIESTE, self.territories.SERBIA), Move(0, Nations.AUSTRIA, self.territories.SERBIA, self.territories.BULGARIA), Move(0, Nations.TURKEY, self.territories.BULGARIA, self.territories.TRIESTE, via_convoy=True), Convoy(0, Nations.TURKEY, self.territories.AEGEAN_SEA, self.territories.BULGARIA, self.territories.TRIESTE), Convoy(0, Nations.TURKEY, self.territories.IONIAN_SEA, self.territories.BULGARIA, self.territories.TRIESTE), Convoy(0, Nations.TURKEY, self.territories.ADRIATIC_SEA, self.territories.BULGARIA, self.territories.TRIESTE), Move(0, Nations.ITALY, self.territories.NAPLES, self.territories.IONIAN_SEA), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.SUCCEEDS) self.assertEqual(pieces[3].dislodged_decision, Outcomes.SUSTAINS) self.assertEqual(pieces[4].dislodged_decision, Outcomes.SUSTAINS) self.assertEqual(pieces[5].dislodged_decision, Outcomes.SUSTAINS) self.assertEqual(orders[6].outcome, Outcomes.FAILS) def test_disrupted_circular_movement_due_to_dislodged_convoy(self): """ When the circular movement contains a convoy, the circular movement is disrupted when the convoying fleet is dislodged. The adjudication algorithm should disrupt convoys before calculating circular movement. Austria: A Trieste - Serbia A Serbia - Bulgaria Turkey: A Bulgaria - Trieste F Aegean Sea Convoys A Bulgaria - Trieste F Ionian Sea Convoys A Bulgaria - Trieste F Adriatic Sea Convoys A Bulgaria - Trieste Italy: F Naples - Ionian Sea F Tunis Supports F Naples - Ionian Sea Due to the dislodged convoying fleet, all Austrian and Turkish armies will not move. """ pieces = [ Army(0, Nations.AUSTRIA, self.territories.TRIESTE), Army(0, Nations.AUSTRIA, self.territories.SERBIA), Army(0, Nations.TURKEY, self.territories.BULGARIA), Fleet(0, Nations.TURKEY, self.territories.AEGEAN_SEA), Fleet(0, Nations.TURKEY, self.territories.IONIAN_SEA), Fleet(0, Nations.TURKEY, self.territories.ADRIATIC_SEA), Fleet(0, Nations.ITALY, self.territories.NAPLES), Fleet(0, Nations.ITALY, self.territories.TUNIS), ] orders = [ Move(0, Nations.AUSTRIA, self.territories.TRIESTE, self.territories.SERBIA), Move(0, Nations.AUSTRIA, self.territories.SERBIA, self.territories.BULGARIA), Move(0, Nations.TURKEY, self.territories.BULGARIA, self.territories.TRIESTE, via_convoy=True), Convoy(0, Nations.TURKEY, self.territories.AEGEAN_SEA, self.territories.BULGARIA, self.territories.TRIESTE), Convoy(0, Nations.TURKEY, self.territories.IONIAN_SEA, self.territories.BULGARIA, self.territories.TRIESTE), Convoy(0, Nations.TURKEY, self.territories.ADRIATIC_SEA, self.territories.BULGARIA, self.territories.TRIESTE), Move(0, Nations.ITALY, self.territories.NAPLES, self.territories.IONIAN_SEA), Support(0, Nations.ITALY, self.territories.TUNIS, self.territories.NAPLES, self.territories.IONIAN_SEA), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].outcome, Outcomes.FAILS) self.assertEqual(orders[1].outcome, Outcomes.FAILS) self.assertEqual(orders[2].outcome, Outcomes.FAILS) self.assertEqual(pieces[3].dislodged_decision, Outcomes.SUSTAINS) self.assertEqual(pieces[4].dislodged_decision, Outcomes.DISLODGED) self.assertEqual(pieces[4].dislodged_by, pieces[6]) self.assertEqual(pieces[5].dislodged_decision, Outcomes.SUSTAINS) self.assertEqual(orders[6].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[7].outcome, Outcomes.SUCCEEDS) def test_two_armies_with_two_convoys(self): """ Two armies can swap places even when they are not adjacent. England: F North Sea Convoys A London - Belgium A London - Belgium France: F English Channel Convoys A Belgium - London A Belgium - London Both convoys should succeed. """ pieces = [ Fleet(0, Nations.ENGLAND, self.territories.NORTH_SEA), Army(0, Nations.ENGLAND, self.territories.LONDON), Fleet(0, Nations.FRANCE, self.territories.ENGLISH_CHANNEL), Army(0, Nations.FRANCE, self.territories.BELGIUM), ] orders = [ Convoy(0, Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.LONDON, self.territories.BELGIUM), Move(0, Nations.ENGLAND, self.territories.LONDON, self.territories.BELGIUM, via_convoy=True), Convoy(0, Nations.FRANCE, self.territories.ENGLISH_CHANNEL, self.territories.BELGIUM, self.territories.LONDON), Move(0, Nations.FRANCE, self.territories.BELGIUM, self.territories.LONDON, via_convoy=True), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[3].outcome, Outcomes.SUCCEEDS) def test_disrupted_unit_swap(self): """ If in a swap one of the unit bounces, then the swap fails. England: F North Sea Convoys A London - Belgium A London - Belgium France: F English Channel Convoys A Belgium - London A Belgium - London A Burgundy - Belgium None of the units will succeed to move. """ pieces = [ Fleet(0, Nations.ENGLAND, self.territories.NORTH_SEA), Army(0, Nations.ENGLAND, self.territories.LONDON), Fleet(0, Nations.FRANCE, self.territories.ENGLISH_CHANNEL), Army(0, Nations.FRANCE, self.territories.BELGIUM), Army(0, Nations.FRANCE, self.territories.BURGUNDY), ] orders = [ Convoy(0, Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.LONDON, self.territories.BELGIUM), Move(0, Nations.ENGLAND, self.territories.LONDON, self.territories.BELGIUM, via_convoy=True), Convoy(0, Nations.FRANCE, self.territories.ENGLISH_CHANNEL, self.territories.BELGIUM, self.territories.LONDON), Move(0, Nations.FRANCE, self.territories.BELGIUM, self.territories.LONDON, via_convoy=True), Move(0, Nations.FRANCE, self.territories.BURGUNDY, self.territories.BELGIUM), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[1].outcome, Outcomes.FAILS) self.assertEqual(orders[3].outcome, Outcomes.FAILS) self.assertEqual(orders[4].outcome, Outcomes.FAILS)
class TestCanRetreat(unittest.TestCase): def setUp(self): self.state = State() self.territories = Territories() self.named_coasts = NamedCoasts(self.territories) self.state = register_all(self.state, self.territories, self.named_coasts) def test_army_all_neighbouring_land_occupied(self): retreating_army = Army(0, Nations.FRANCE, self.territories.PORTUGAL) spain_army = Army(0, Nations.ITALY, self.territories.SPAIN) self.state.register(retreating_army, spain_army) self.state.post_register_updates() self.assertFalse(retreating_army.can_retreat()) def test_army_neighbouring_land_contested(self): retreating_army = Army(0, Nations.FRANCE, self.territories.BREST) gascony_army = Army(0, Nations.ITALY, self.territories.GASCONY) picary_army = Army(0, Nations.ITALY, self.territories.PICARDY) self.state.register(retreating_army, gascony_army, picary_army) self.state.post_register_updates() paris = self.state.get_territory('paris') paris.bounce_occurred = True self.assertFalse(retreating_army.can_retreat()) def test_fleet_all_neighbouring_seas_occupied(self): retreating_fleet = Fleet(0, Nations.FRANCE, self.territories.BARRENTS_SEA) stp_army = Army(0, Nations.RUSSIA, self.territories.ST_PETERSBURG) norway_army = Army(0, Nations.RUSSIA, self.territories.NORWAY) norwegian_sea_fleet = Fleet(0, Nations.RUSSIA, self.territories.NORWEGIAN_SEA) self.state.register(retreating_fleet, stp_army, norway_army, norwegian_sea_fleet) self.state.post_register_updates() self.assertFalse(retreating_fleet.can_retreat()) def test_fleet_can_retreat_to_coast(self): retreating_fleet = Fleet(0, Nations.FRANCE, self.territories.BARRENTS_SEA) norway_army = Army(0, Nations.RUSSIA, self.territories.NORWAY) norwegian_sea_fleet = Fleet(0, Nations.RUSSIA, self.territories.NORWEGIAN_SEA) self.state.register(retreating_fleet, norway_army, norwegian_sea_fleet) self.state.post_register_updates() self.assertTrue(retreating_fleet.can_retreat())
class TestRetreating(unittest.TestCase): def setUp(self): self.state = State() self.territories = Territories() self.named_coasts = NamedCoasts(self.territories) self.state = register_all(self.state, self.territories, self.named_coasts) def test_fleets_cannot_be_built_inland(self): """ Physically this is possible, but it is still not allowed. Russia has one build and Moscow is empty. Russia: Build F Moscow See issue 4.C.4. Some game masters will change the order and build an army in Moscow. I prefer that the build fails. """ orders = [ Build(0, Nations.RUSSIA, self.territories.MOSCOW, PieceTypes.FLEET), ] self.state.register(*orders) self.state.post_register_updates() process(self.state) self.assertTrue(orders[0].illegal) self.assertEqual(orders[0].illegal_code, '015') self.assertEqual(orders[0].illegal_verbose, 'Piece type cannot exist in this type of territory.') def test_supply_center_must_be_empty_for_building(self): """ You can't have two units in a sector. So, you can't build when there is a unit in the supply center. Germany may build a unit but has an army in Berlin. Germany orders the following: Germany: Build A Berlin Build fails. """ pieces = [ Army(0, Nations.GERMANY, self.territories.BERLIN), ] orders = [ Build(0, Nations.GERMANY, self.territories.BERLIN, PieceTypes.ARMY), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertTrue(orders[0].illegal) self.assertEqual(orders[0].illegal_code, '011') self.assertEqual(orders[0].illegal_verbose, 'Source is already occupied by a piece.') def test_both_coasts_must_be_empty_for_building(self): """ If a sector is occupied on one coast, the other coast can not be used for building. Russia may build a unit and has a fleet in St Petersburg(sc). Russia orders the following: Russia: Build A St Petersburg(nc) Build fails. """ pieces = [ Fleet(0, Nations.RUSSIA, self.territories.ST_PETERSBURG, self.named_coasts.ST_PETERSBURG_NC), ] orders = [ Build(0, Nations.RUSSIA, self.territories.ST_PETERSBURG, PieceTypes.FLEET, self.named_coasts.ST_PETERSBURG_SC), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertTrue(orders[0].illegal) self.assertEqual(orders[0].illegal_code, '011') self.assertEqual(orders[0].illegal_verbose, 'Source is already occupied by a piece.') def test_building_in_home_supply_center_that_is_not_owned(self): """ Building a unit is only allowed when supply center is a home supply center and is owned. If not owned, build fails. """ self.territories.ST_PETERSBURG.controlled_by = Nations.GERMANY orders = [ Build(0, Nations.RUSSIA, self.territories.ST_PETERSBURG, PieceTypes.FLEET, self.named_coasts.ST_PETERSBURG_SC), ] self.state.register(*orders) self.state.post_register_updates() process(self.state) self.assertTrue(orders[0].illegal) self.assertEqual(orders[0].illegal_code, '014') self.assertEqual( orders[0].illegal_verbose, ('Cannot build in a supply center which is controlled by a ' 'foreign power.')) def test_building_in_owned_supply_center_that_is_not_home(self): """ Building a unit is only allowed when supply center is a home supply center and is owned. If it is not a home supply center, the build fails. """ self.territories.ST_PETERSBURG.controlled_by = Nations.GERMANY orders = [ Build(0, Nations.GERMANY, self.territories.ST_PETERSBURG, PieceTypes.FLEET, self.named_coasts.ST_PETERSBURG_SC), ] self.state.register(*orders) self.state.post_register_updates() process(self.state) self.assertTrue(orders[0].illegal) self.assertEqual(orders[0].illegal_code, '013') self.assertEqual(orders[0].illegal_verbose, 'Source is outside of national borders.')
class TestConvoyingToAdjacentPlaces(unittest.TestCase): def setUp(self): self.state = State() self.territories = Territories() self.named_coasts = NamedCoasts(self.territories) self.state = register_all(self.state, self.territories, self.named_coasts) def test_two_units_can_swap_places_by_convoy(self): """ The only way to swap two units, is by convoy. England: A Norway - Sweden F Skagerrak Convoys A Norway - Sweden Russia: A Sweden - Norway In most interpretation of the rules, the units in Norway and Sweden will be swapped. However, if explicit adjacent convoying is used (see issue 4.A.3), then it is just a head to head battle. I prefer the 2000 rules, so the units are swapped. """ pieces = [ Army(0, Nations.ENGLAND, self.territories.NORWAY), Fleet(0, Nations.ENGLAND, self.territories.SKAGERRAK), Army(0, Nations.RUSSIA, self.territories.SWEDEN), ] orders = [ Move(0, Nations.ENGLAND, self.territories.NORWAY, self.territories.SWEDEN, via_convoy=True), Convoy(0, Nations.ENGLAND, self.territories.SKAGERRAK, self.territories.NORWAY, self.territories.SWEDEN), Move(0, Nations.RUSSIA, self.territories.SWEDEN, self.territories.NORWAY), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].path_decision(), Outcomes.PATH) self.assertEqual(orders[0].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.SUCCEEDS) def test_kidnapping_an_army(self): """ Germany promised England to support to dislodge the Russian fleet in Sweden and it promised Russia to support to dislodge the English army in Norway. Instead, the joking German orders a convoy. England: A Norway - Sweden Russia: F Sweden - Norway Germany: F Skagerrak Convoys A Norway - Sweden See issue 4.A.3. When the 1982/2000 rulebook is used (which I prefer), England has no intent to swap and it is just a head to head battle were both units will fail to move. When explicit adjacent convoying is used (DPTG), the English move is not a convoy and again it just a head to head battle were both units will fail to move. In all other interpretations, the army in Norway will be convoyed and swap its place with the fleet in Sweden. """ pieces = [ Army(0, Nations.ENGLAND, self.territories.NORWAY), Fleet(0, Nations.RUSSIA, self.territories.SKAGERRAK), Army(0, Nations.RUSSIA, self.territories.SWEDEN), ] orders = [ Move(0, Nations.ENGLAND, self.territories.NORWAY, self.territories.SWEDEN), Convoy(0, Nations.RUSSIA, self.territories.SKAGERRAK, self.territories.NORWAY, self.territories.SWEDEN), Move(0, Nations.RUSSIA, self.territories.SWEDEN, self.territories.NORWAY), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].outcome, Outcomes.FAILS) self.assertEqual(orders[2].outcome, Outcomes.FAILS) def test_kidnapping_with_a_disrupted_convoy(self): """ When kidnapping of armies is allowed, a move can be sabotaged by a fleet that is almost certainly dislodged. France: F Brest - English Channel A Picardy - Belgium A Burgundy Supports A Picardy - Belgium F Mid-Atlantic Ocean Supports F Brest - English Channel England: F English Channel Convoys A Picardy - Belgium See issue 4.A.3. If a convoy always takes precedence over a land route (choice a), the move from Picardy to Belgium fails. It tries to convoy and the convoy is disrupted. For choice b and c, there is no unit moving in opposite direction for the move of the army in Picardy. For this reason, the move for the army in Picardy is not by convoy and succeeds over land. When the 1982 or 2000 rules are used (choice d), then it is not the "intent" of the French army in Picardy to convoy. The move from Picardy to Belgium is just a successful move over land. When explicit adjacent convoying is used (DPTG, choice e), the order of the French army in Picardy is not a convoy order. So, it just ordered over land, and that move succeeds. This is an excellent example why the convoy route should not automatically have priority over the land route. It would just be annoying for the attacker and this situation is without fun. I prefer the 1982 rule with the 2000 clarification. According to these rules the move from Picardy succeeds. """ pieces = [ Fleet(0, Nations.FRANCE, self.territories.BREST), Army(0, Nations.FRANCE, self.territories.PICARDY), Army(0, Nations.FRANCE, self.territories.BURGUNDY), Fleet(0, Nations.FRANCE, self.territories.MID_ATLANTIC), Fleet(0, Nations.ENGLAND, self.territories.ENGLISH_CHANNEL), ] orders = [ Move(0, Nations.FRANCE, self.territories.BREST, self.territories.ENGLISH_CHANNEL), Move(0, Nations.FRANCE, self.territories.PICARDY, self.territories.BELGIUM), Support(0, Nations.FRANCE, self.territories.BURGUNDY, self.territories.PICARDY, self.territories.BELGIUM), Support(0, Nations.FRANCE, self.territories.MID_ATLANTIC, self.territories.BREST, self.territories.ENGLISH_CHANNEL), Convoy(0, Nations.ENGLAND, self.territories.ENGLISH_CHANNEL, self.territories.PICARDY, self.territories.BELGIUM), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[3].outcome, Outcomes.SUCCEEDS) def test_kidnapping_with_a_disrupted_convoy_and_opposite_move(self): """ In the situation of the previous test case it was rather clear that the army didn't want to take the convoy. But what if there is an army moving in opposite direction? France: F Brest - English Channel A Picardy - Belgium A Burgundy Supports A Picardy - Belgium F Mid-Atlantic Ocean Supports F Brest - English Channel England: F English Channel Convoys A Picardy - Belgium A Belgium - Picardy See issue 4.A.3. If a convoy always takes precedence over a land route (choice a), the move from Picardy to Belgium fails. It tries to convoy and the convoy is disrupted. For choice b the convoy is also taken, because there is a unit in Belgium moving in opposite direction. This means that the convoy is disrupted and the move from Picardy to Belgium fails. For choice c the convoy is not taken. Although, the unit in Belgium is moving in opposite direction, the army will not take a disrupted convoy. So, the move from Picardy to Belgium succeeds. When the 1982 or 2000 rules are used (choice d), then it is not the "intent" of the French army in Picardy to convoy. The move from Picardy to Belgium is just a successful move over land. When explicit adjacent convoying is used (DPTG, choice e), the order of the French army in Picardy is not a convoy order. So, it just ordered over land, and that move succeeds. Again an excellent example why the convoy route should not automatically have priority over the land route. It would just be annoying for the attacker and this situation is without fun. I prefer the 1982 rule with the 2000 clarification. According to these rules the move from Picardy succeeds. """ pieces = [ Fleet(0, Nations.FRANCE, self.territories.BREST), Army(0, Nations.FRANCE, self.territories.PICARDY), Army(0, Nations.FRANCE, self.territories.BURGUNDY), Fleet(0, Nations.FRANCE, self.territories.MID_ATLANTIC), Fleet(0, Nations.ENGLAND, self.territories.ENGLISH_CHANNEL), Army(0, Nations.ENGLAND, self.territories.BELGIUM), ] orders = [ Move(0, Nations.FRANCE, self.territories.BREST, self.territories.ENGLISH_CHANNEL), Move(0, Nations.FRANCE, self.territories.PICARDY, self.territories.BELGIUM), Support(0, Nations.FRANCE, self.territories.BURGUNDY, self.territories.PICARDY, self.territories.BELGIUM), Support(0, Nations.FRANCE, self.territories.MID_ATLANTIC, self.territories.BREST, self.territories.ENGLISH_CHANNEL), Convoy(0, Nations.ENGLAND, self.territories.ENGLISH_CHANNEL, self.territories.PICARDY, self.territories.BELGIUM), Move(0, Nations.ENGLAND, self.territories.BELGIUM, self.territories.PICARDY), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[3].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[5].outcome, Outcomes.FAILS) self.assertEqual(pieces[5].dislodged_decision, Outcomes.DISLODGED) 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. """ pieces = [ Army(0, Nations.ENGLAND, self.territories.LIVERPOOL), Fleet(0, Nations.ENGLAND, self.territories.ENGLISH_CHANNEL), Army(0, Nations.GERMANY, self.territories.EDINBURGH), Fleet(0, Nations.FRANCE, self.territories.IRISH_SEA), Fleet(0, Nations.FRANCE, self.territories.NORTH_SEA), Fleet(0, Nations.RUSSIA, self.territories.NORWEGIAN_SEA), Fleet(0, Nations.RUSSIA, self.territories.NORTH_ATLANTIC), ] orders = [ Move(0, Nations.ENGLAND, self.territories.LIVERPOOL, self.territories.EDINBURGH), Convoy(0, Nations.ENGLAND, self.territories.ENGLISH_CHANNEL, self.territories.LIVERPOOL, self.territories.EDINBURGH), Move(0, Nations.GERMANY, self.territories.EDINBURGH, self.territories.LIVERPOOL), Hold(0, Nations.FRANCE, self.territories.IRISH_SEA), Hold(0, Nations.FRANCE, self.territories.NORTH_SEA), Convoy(0, Nations.RUSSIA, self.territories.NORWEGIAN_SEA, self.territories.LIVERPOOL, self.territories.EDINBURGH), Convoy(0, Nations.RUSSIA, self.territories.NORTH_ATLANTIC, self.territories.LIVERPOOL, self.territories.EDINBURGH), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].outcome, Outcomes.FAILS) self.assertEqual(orders[2].outcome, Outcomes.FAILS) def test_swapping_with_illegal_intent(self): """ Can the intent made clear with an impossible order? England: F Skagerrak Convoys A Sweden - Norway F Norway - Sweden Russia: A Sweden - Norway F Gulf of Bothnia Convoys A Sweden - Norway See issue 4.A.3 and 4.E.1. If for issue 4.A.3 choice a, b or c has been taken, then the army in weden moves by convoy and swaps places with the fleet in Norway. However, if for issue 4.A.3 the 1982/2000 has been chosen (choice d), then the "intent" is important. The question is whether the fleet in the Gulf of Bothnia can express the intent. If the order for this fleet is considered illegal (see issue 4.E.1), then this order must be ignored and there is no intent to swap. In that case none of the units move. If explicit convoying is used (DPTG, choice e of issue 4.A.3) then the army in Sweden will take the land route and none of the units move. I prefer the 1982/2000 rule and that any orders that can't be valid are illegal. So, the order of the fleet in the Gulf of Bothnia is ignored and can not show the intent. There is no convoy, so no unit will move. """ pieces = [ Fleet(0, Nations.ENGLAND, self.territories.SKAGERRAK), Fleet(0, Nations.ENGLAND, self.territories.NORWAY), Army(0, Nations.RUSSIA, self.territories.SWEDEN), Fleet(0, Nations.RUSSIA, self.territories.GULF_OF_BOTHNIA), ] orders = [ Convoy(0, Nations.ENGLAND, self.territories.SKAGERRAK, self.territories.SWEDEN, self.territories.NORWAY), Move(0, Nations.ENGLAND, self.territories.NORWAY, self.territories.SWEDEN), Move(0, Nations.RUSSIA, self.territories.SWEDEN, self.territories.NORWAY), Convoy(0, Nations.RUSSIA, self.territories.GULF_OF_BOTHNIA, self.territories.SWEDEN, self.territories.NORWAY), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[1].outcome, Outcomes.FAILS) self.assertEqual(orders[2].outcome, Outcomes.FAILS) def test_explicit_convoy_that_isnt_there(self): """ What to do when a unit is explicitly ordered to move via convoy and the convoy is not there? France: A Belgium - Holland via Convoy England: F North Sea - Helgoland Bight A Holland - Kiel The French army in Belgium intended to move convoyed with the English fleet in the North Sea. But the English changed their plans. See issue 4.A.3. If choice a, b or c has been taken, then the 'via Convoy' directive has no meaning and the army in Belgium will move to Holland. If the 1982/2000 rulebook is used (choice d, which I prefer), the "via Convoy" has meaning, but only when there is both a land route and a convoy route. Since there is no convoy the "via Convoy" directive should be ignored. And the move from Belgium to Holland succeeds. If explicit adjacent convoying is used (DPTG, choice e), then the unit can only go by convoy. Since there is no convoy, the move from Belgium to Holland fails. """ pieces = [ Army(0, Nations.FRANCE, self.territories.BELGIUM), Fleet(0, Nations.ENGLAND, self.territories.NORTH_SEA), Army(0, Nations.ENGLAND, self.territories.HOLLAND), ] orders = [ Move(0, Nations.FRANCE, self.territories.BELGIUM, self.territories.HOLLAND, via_convoy=True), Move(0, Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.HELGOLAND_BIGHT), Move(0, Nations.ENGLAND, self.territories.HOLLAND, self.territories.KIEL), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].path_decision(), Outcomes.NO_PATH) self.assertEqual(orders[0].outcome, Outcomes.FAILS) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.SUCCEEDS) def test_swapped_or_dislodged(self): """ The 1982 rulebook says that whether the move is over land or via convoy depends on the "intent" as shown by the totality of the orders written by the player governing the army (see issue 4.A.3). In this test case the English army in Norway will end in all cases in Sweden. But whether it is convoyed or not has effect on the Russian army. In case of convoy the Russian army ends in Norway and in case of a land route the Russian army is dislodged. England: A Norway - Sweden F Skagerrak Convoys A Norway - Sweden F Finland Supports A Norway - Sweden Russia: A Sweden - Norway See issue 4.A.3. For choice a, b and c the move of the army in Norway is by convoy and the armies in Norway and Sweden are swapped. If the 1982 rulebook is used with the clarification of the 2000 rulebook (choice d, which I prefer), the intent of the English player is to convoy, since it ordered the fleet in Skagerrak to convoy. Therefore, the armies in Norway and Sweden are swapped. When explicit adjacent convoying is used (DTPG, choice e), then the unit in Norway did not receive an order to move by convoy and the land route should be considered. The Russian army in Sweden is dislodged. """ pieces = [ Army(0, Nations.ENGLAND, self.territories.NORWAY), Fleet(0, Nations.ENGLAND, self.territories.SKAGERRAK), Fleet(0, Nations.ENGLAND, self.territories.FINLAND), Army(0, Nations.RUSSIA, self.territories.SWEDEN), ] orders = [ Move(0, Nations.ENGLAND, self.territories.NORWAY, self.territories.SWEDEN), Convoy(0, Nations.ENGLAND, self.territories.SKAGERRAK, self.territories.NORWAY, self.territories.SWEDEN), Support(0, Nations.ENGLAND, self.territories.FINLAND, self.territories.NORWAY, self.territories.SWEDEN), Move(0, Nations.RUSSIA, self.territories.SWEDEN, self.territories.NORWAY), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.SUCCEEDS) self.assertEqual(pieces[3].dislodged_decision, Outcomes.DISLODGED) @unittest.skip('convoy swap') def test_swapped_or_head_to_head(self): """ Can a dislodged unit have effect on the attackers area, when the attacker moved by convoy? England: A Norway - Sweden via Convoy F Denmark Supports A Norway - Sweden F Finland Supports A Norway - Sweden Germany: F Skagerrak Convoys A Norway - Sweden Russia: A Sweden - Norway F Barents Sea Supports A Sweden - Norway France: F Norwegian Sea - Norway F North Sea Supports F Norwegian Sea - Norway Since England ordered the army in Norway to move explicitly via convoy and the army in Sweden is moving in opposite direction, only the convoyed route should be considered regardless of the rulebook used. It is clear that the army in Norway will dislodge the Russian army in Sweden. Since the strength of three is in all cases the strongest force. The army in Sweden will not advance to Norway, because it can not beat the force in the Norwegian Sea. It will be dislodged by the army from Norway. The more interesting question is whether French fleet in the Norwegian Sea is bounced by the Russian army from Sweden. This depends on the interpretation of issue 4.A.7. If the rulebook is taken literally (choice a), then a dislodged unit can not bounce a unit in the area where the attacker came from. This would mean that the move of the fleet in the Norwegian Sea succeeds However, if choice b is taken (which I prefer), then a bounce is still possible, when there is no head to head battle. So, the fleet in the Norwegian Sea will fail to move. """ pieces = [ Army(0, Nations.ENGLAND, self.territories.NORWAY), Fleet(0, Nations.ENGLAND, self.territories.DENMARK), Fleet(0, Nations.ENGLAND, self.territories.FINLAND), Fleet(0, Nations.GERMANY, self.territories.SKAGERRAK), Army(0, Nations.RUSSIA, self.territories.SWEDEN), Fleet(0, Nations.RUSSIA, self.territories.BARRENTS_SEA), Fleet(0, Nations.FRANCE, self.territories.NORWEGIAN_SEA), Fleet(0, Nations.FRANCE, self.territories.NORTH_SEA), ] orders = [ Move(0, Nations.ENGLAND, self.territories.NORWAY, self.territories.SWEDEN, via_convoy=True), Support(0, Nations.ENGLAND, self.territories.DENMARK, self.territories.NORWAY, self.territories.SWEDEN), Support(0, Nations.ENGLAND, self.territories.FINLAND, self.territories.NORWAY, self.territories.SWEDEN), Convoy(0, Nations.GERMANY, self.territories.SKAGERRAK, self.territories.NORWAY, self.territories.SWEDEN), Move(0, Nations.RUSSIA, self.territories.SWEDEN, self.territories.NORWAY), Support(0, Nations.RUSSIA, self.territories.BARRENTS_SEA, self.territories.SWEDEN, self.territories.NORWAY), Move(0, Nations.FRANCE, self.territories.NORWEGIAN_SEA, self.territories.NORWAY), Support(0, Nations.FRANCE, self.territories.NORTH_SEA, self.territories.NORWEGIAN_SEA, self.territories.NORWAY), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[4].outcome, Outcomes.FAILS) self.assertEqual(pieces[4].dislodged_decision, Outcomes.DISLODGED) self.assertEqual(orders[5].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[6].outcome, Outcomes.FAILS) self.assertEqual(orders[7].outcome, Outcomes.SUCCEEDS) @unittest.skip( 'test_convoy_to_an_adjacent_place_with_paradox - convoy paradox') def test_convoy_to_an_adjacent_place_with_paradox(self): """ In this case the convoy route is available when the land route is chosen and the convoy route is not available when the convoy route is chosen. England: F Norway Supports F North Sea - Skagerrak F North Sea - Skagerrak Russia: A Sweden - Norway F Skagerrak Convoys A Sweden - Norway F Barents Sea Supports A Sweden - Norway See issue 4.A.2 and 4.A.3. If for issue 4.A.3, choice b, c or e has been taken, then the move from Sweden to Norway is not a convoy and the English fleet in Norway is dislodged and the fleet in Skagerrak will not be dislodged. If choice a or d (1982/2000 rule) has been taken for issue 4.A.3, then the move from Sweden to Norway must be treated as a convoy. At that moment the situation becomes paradoxical. When the 'All Hold' rule is used, both the army in Sweden as the fleet in the North Sea will not advance. In all other paradox rules the English fleet in the North Sea will dislodge the Russian fleet in Skagerrak and the army in Sweden will not advance. I prefer the 1982 rule with the 2000 rulebook clarification concerning the convoy to adjacent places and I prefer the Szykman rule for paradox resolving. That means that according to these preferences the fleet in the North Sea will dislodge the Russian fleet in Skagerrak and the army in Sweden will not advance. """ pieces = [ Fleet(0, Nations.ENGLAND, self.territories.NORWAY), Fleet(0, Nations.ENGLAND, self.territories.NORTH_SEA), Army(0, Nations.RUSSIA, self.territories.SWEDEN), Fleet(0, Nations.RUSSIA, self.territories.SKAGERRAK), Fleet(0, Nations.RUSSIA, self.territories.BARRENTS_SEA), ] orders = [ Support(0, Nations.ENGLAND, self.territories.NORWAY, self.territories.NORTH_SEA, self.territories.SKAGERRAK), Move(0, Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.SKAGERRAK), Move(0, Nations.RUSSIA, self.territories.SWEDEN, self.territories.NORWAY), Convoy(0, Nations.RUSSIA, self.territories.SKAGERRAK, self.territories.SWEDEN, self.territories.NORWAY), Support(0, Nations.RUSSIA, self.territories.BARRENTS_SEA, self.territories.SWEDEN, self.territories.NORWAY), ] self.state.register(*pieces, *orders) self.state.post_register_updates() 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.SUCCEEDS) def test_swapping_two_units_with_two_convoys(self): """ Of course, two armies can also swap by when they are both convoyed. England: A Liverpool - Edinburgh via Convoy F North Atlantic Ocean Convoys A Liverpool - Edinburgh F Norwegian Sea Convoys A Liverpool - Edinburgh Germany: A Edinburgh - Liverpool via Convoy F North Sea Convoys A Edinburgh - Liverpool F English Channel Convoys A Edinburgh - Liverpool F Irish Sea Convoys A Edinburgh - Liverpool The armies in Liverpool and Edinburgh are swapped. """ pieces = [ Army(0, Nations.ENGLAND, self.territories.LIVERPOOL), Fleet(0, Nations.ENGLAND, self.territories.NORTH_ATLANTIC), Fleet(0, Nations.ENGLAND, self.territories.NORWEGIAN_SEA), Army(0, Nations.GERMANY, self.territories.EDINBURGH), Fleet(0, Nations.GERMANY, self.territories.NORTH_SEA), Fleet(0, Nations.GERMANY, self.territories.ENGLISH_CHANNEL), Fleet(0, Nations.GERMANY, self.territories.IRISH_SEA), ] orders = [ Move(0, Nations.ENGLAND, self.territories.LIVERPOOL, self.territories.EDINBURGH, via_convoy=True), Convoy(0, Nations.ENGLAND, self.territories.NORTH_ATLANTIC, self.territories.LIVERPOOL, self.territories.EDINBURGH), Convoy(0, Nations.ENGLAND, self.territories.NORWEGIAN_SEA, self.territories.LIVERPOOL, self.territories.EDINBURGH), Move(0, Nations.GERMANY, self.territories.EDINBURGH, self.territories.LIVERPOOL, via_convoy=True), Convoy(0, Nations.GERMANY, self.territories.NORTH_SEA, self.territories.EDINBURGH, self.territories.LIVERPOOL), Convoy(0, Nations.GERMANY, self.territories.ENGLISH_CHANNEL, self.territories.EDINBURGH, self.territories.LIVERPOOL), Convoy(0, Nations.GERMANY, self.territories.IRISH_SEA, self.territories.EDINBURGH, self.territories.LIVERPOOL), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].path_decision(), Outcomes.PATH) self.assertEqual(orders[0].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[3].path_decision(), Outcomes.PATH) self.assertEqual(orders[3].outcome, Outcomes.SUCCEEDS) def test_support_cut_on_itself_via_convoy(self): """ If a unit is attacked by a supported unit, it is not possible to prevent dislodgement by trying to cut the support. But what, if a move is attempted via a convoy? Austria: F Adriatic Sea Convoys A Trieste - Venice A Trieste - Venice via Convoy Italy: A Venice Supports F Albania - Trieste F Albania - Trieste First it should be mentioned that if for issue 4.A.3 choice b or c is taken, then the move from Trieste to Venice is just a move over land, because the army in Venice is not moving in opposite direction. In that case, the support of Venice will not be cut as normal. In any other choice for issue 4.A.3, it should be decided whether the Austrian attack is considered to be coming from Trieste or from the Adriatic Sea. If it comes from Trieste, the support in Venice is not cut and the army in Trieste is dislodged by the fleet in Albania. If the Austrian attack is considered to be coming from the Adriatic Sea, then the support is cut and the army in Trieste will not be dislodged. See also issue 4.A.4. First of all, I prefer the 1982/2000 rules for adjacent convoying. This means that I prefer the move from Trieste uses the convoy. Furthermore, I think that the two Italian units are still stronger than the army in Trieste. Therefore, I prefer that the support in Venice is not cut and that the army in Trieste is dislodged by the fleet in Albania. """ pieces = [ Fleet(0, Nations.AUSTRIA, self.territories.ADRIATIC_SEA), Army(0, Nations.AUSTRIA, self.territories.TRIESTE), Army(0, Nations.ITALY, self.territories.VENICE), Fleet(0, Nations.ITALY, self.territories.ALBANIA), ] orders = [ Convoy(0, Nations.AUSTRIA, self.territories.ADRIATIC_SEA, self.territories.TRIESTE, self.territories.VENICE), Move(0, Nations.AUSTRIA, self.territories.TRIESTE, self.territories.VENICE, via_convoy=True), Support(0, Nations.ITALY, self.territories.VENICE, self.territories.ALBANIA, self.territories.TRIESTE), Move(0, Nations.ITALY, self.territories.ALBANIA, self.territories.TRIESTE), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(pieces[1].dislodged_decision, Outcomes.DISLODGED) self.assertEqual(orders[2].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[3].outcome, Outcomes.SUCCEEDS) def test_bounce_by_convoy_to_adjacent_place(self): """ Similar to test case 6.G.10, but now the other unit is taking the convoy. England: A Norway - Sweden F Denmark Supports A Norway - Sweden F Finland Supports A Norway - Sweden France: F Norwegian Sea - Norway F North Sea Supports F Norwegian Sea - Norway Germany: F Skagerrak Convoys A Sweden - Norway Russia: A Sweden - Norway via Convoy F Barents Sea Supports A Sweden - Norway Again the army in Sweden is bounced by the fleet in the Norwegian Sea. The army in Norway will move to Sweden and dislodge the Russian army. The final destination of the fleet in the Norwegian Sea depends on how issue 4.A.7 is resolved. If choice a is taken, then the fleet advances to Norway, but if choice b is taken (which I prefer) the fleet bounces and stays in the Norwegian Sea. """ pieces = [ Army(0, Nations.ENGLAND, self.territories.NORWAY), Fleet(0, Nations.ENGLAND, self.territories.DENMARK), Fleet(0, Nations.ENGLAND, self.territories.FINLAND), Fleet(0, Nations.FRANCE, self.territories.NORWEGIAN_SEA), Fleet(0, Nations.FRANCE, self.territories.NORTH_SEA), Fleet(0, Nations.GERMANY, self.territories.SKAGERRAK), Army(0, Nations.RUSSIA, self.territories.SWEDEN), Fleet(0, Nations.RUSSIA, self.territories.BARRENTS_SEA), ] orders = [ Move(0, Nations.ENGLAND, self.territories.NORWAY, self.territories.SWEDEN), Support(0, Nations.ENGLAND, self.territories.DENMARK, self.territories.NORWAY, self.territories.SWEDEN), Support(0, Nations.ENGLAND, self.territories.FINLAND, self.territories.NORWAY, self.territories.SWEDEN), Move(0, Nations.FRANCE, self.territories.NORWEGIAN_SEA, self.territories.NORWAY), Support(0, Nations.FRANCE, self.territories.NORTH_SEA, self.territories.NORWEGIAN_SEA, self.territories.NORWAY), Convoy(0, Nations.GERMANY, self.territories.SKAGERRAK, self.territories.SWEDEN, self.territories.NORWAY), Move(0, Nations.RUSSIA, self.territories.SWEDEN, self.territories.NORWAY, via_convoy=True), Support(0, Nations.RUSSIA, self.territories.BARRENTS_SEA, self.territories.SWEDEN, self.territories.NORWAY), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[3].outcome, Outcomes.FAILS) self.assertEqual(orders[4].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[6].outcome, Outcomes.FAILS) self.assertEqual(pieces[6].dislodged_decision, Outcomes.DISLODGED) self.assertEqual(orders[7].outcome, Outcomes.SUCCEEDS) @unittest.skip('Convoy swaps should be handled by a separate runner') def test_bounce_and_dislodge_with_double_convoy(self): """ Similar to test case 6.G.10, but now both units use a convoy and without some support. England: F North Sea Convoys A London - Belgium A Holland Supports A London - Belgium A Yorkshire - London A London - Belgium via Convoy France: F English Channel Convoys A Belgium - London A Belgium - London via Convoy The French army in Belgium is bounced by the army from Yorkshire. The army in London move to Belgium, dislodging the unit there. The final destination of the army in the Yorkshire depends on how issue 4.A.7 is resolved. If choice a is taken, then the army advances to London, but if choice b is taken (which I prefer) the army bounces and stays in Yorkshire. """ pieces = [ Fleet(0, Nations.ENGLAND, self.territories.NORTH_SEA), Army(0, Nations.ENGLAND, self.territories.HOLLAND), Army(0, Nations.ENGLAND, self.territories.YORKSHIRE), Army(0, Nations.ENGLAND, self.territories.LONDON), Fleet(0, Nations.FRANCE, self.territories.ENGLISH_CHANNEL), Army(0, Nations.FRANCE, self.territories.BELGIUM), ] orders = [ Convoy(0, Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.LONDON, self.territories.BELGIUM), Support(0, Nations.ENGLAND, self.territories.HOLLAND, self.territories.LONDON, self.territories.BELGIUM), Move(0, Nations.ENGLAND, self.territories.YORKSHIRE, self.territories.LONDON), Move(0, Nations.ENGLAND, self.territories.LONDON, self.territories.BELGIUM, via_convoy=True), Convoy(0, Nations.FRANCE, self.territories.ENGLISH_CHANNEL, self.territories.BELGIUM, self.territories.LONDON), Move(0, Nations.FRANCE, self.territories.BELGIUM, self.territories.LONDON, via_convoy=True), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.FAILS) self.assertEqual(orders[3].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[5].outcome, Outcomes.FAILS) self.assertEqual(pieces[5].dislodged_decision, Outcomes.DISLODGED) def test_two_unit_in_one_area_bug_moving_by_convoy(self): """ If the adjudicator is not correctly implemented, this may lead to a resolution where two units end up in the same area. England: A Norway - Sweden A Denmark Supports A Norway - Sweden F Baltic Sea Supports A Norway - Sweden F North Sea - Norway Russia: A Sweden - Norway via Convoy F Skagerrak Convoys A Sweden - Norway F Norwegian Sea Supports A Sweden - Norway See decision details 5.B.6. If the 'PREVENT STRENGTH' is incorrectly implemented, due to the fact that it does not take into account that the 'PREVENT STRENGTH' is only zero when the unit is engaged in a head to head battle, then this goes wrong in this test case. The 'PREVENT STRENGTH' of Sweden would be zero, because the opposing unit in Norway successfully moves. Since, this strength would be zero, the fleet in the North Sea would move to Norway. However, although the 'PREVENT STRENGTH' is zero, the army in Sweden would also move to Norway. So, the final result would contain two units that successfully moved to Norway. Of course, this is incorrect. Norway will indeed successfully move to Sweden while the army in Sweden ends in Norway, because it is stronger then the fleet in the North Sea. This fleet will stay in the North Sea. """ pieces = [ Army(0, Nations.ENGLAND, self.territories.NORWAY), Fleet(0, Nations.ENGLAND, self.territories.DENMARK), Fleet(0, Nations.ENGLAND, self.territories.BALTIC_SEA), Fleet(0, Nations.ENGLAND, self.territories.NORTH_SEA), Army(0, Nations.RUSSIA, self.territories.SWEDEN), Fleet(0, Nations.RUSSIA, self.territories.SKAGERRAK), Fleet(0, Nations.RUSSIA, self.territories.NORWEGIAN_SEA), ] orders = [ Move(0, Nations.ENGLAND, self.territories.NORWAY, self.territories.SWEDEN), Support(0, Nations.ENGLAND, self.territories.DENMARK, self.territories.NORWAY, self.territories.SWEDEN), Support(0, Nations.ENGLAND, self.territories.BALTIC_SEA, self.territories.NORWAY, self.territories.SWEDEN), Move(0, Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.NORWAY), Move(0, Nations.RUSSIA, self.territories.SWEDEN, self.territories.NORWAY, via_convoy=True), Convoy(0, Nations.RUSSIA, self.territories.SKAGERRAK, self.territories.SWEDEN, self.territories.NORWAY), Support(0, Nations.RUSSIA, self.territories.NORWEGIAN_SEA, self.territories.SWEDEN, self.territories.NORWAY), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[3].outcome, Outcomes.FAILS) self.assertEqual(orders[4].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[6].outcome, Outcomes.SUCCEEDS) def test_two_unit_in_one_area_bug_moving_by_land(self): """ Similar to the previous test case, but now the other unit moves by convoy. England: A Norway - Sweden via Convoy A Denmark Supports A Norway - Sweden F Baltic Sea Supports A Norway - Sweden F Skagerrak Convoys A Norway - Sweden F North Sea - Norway Russia: A Sweden - Norway F Norwegian Sea Supports A Sweden - Norway Sweden and Norway are swapped, while the fleet in the North Sea will bounce. """ pieces = [ Army(0, Nations.ENGLAND, self.territories.NORWAY), Fleet(0, Nations.ENGLAND, self.territories.DENMARK), Fleet(0, Nations.ENGLAND, self.territories.BALTIC_SEA), Fleet(0, Nations.ENGLAND, self.territories.SKAGERRAK), Fleet(0, Nations.ENGLAND, self.territories.NORTH_SEA), Army(0, Nations.RUSSIA, self.territories.SWEDEN), Fleet(0, Nations.RUSSIA, self.territories.NORWEGIAN_SEA), ] orders = [ Move(0, Nations.ENGLAND, self.territories.NORWAY, self.territories.SWEDEN, via_convoy=True), Support(0, Nations.ENGLAND, self.territories.DENMARK, self.territories.NORWAY, self.territories.SWEDEN), Support(0, Nations.ENGLAND, self.territories.BALTIC_SEA, self.territories.NORWAY, self.territories.SWEDEN), Convoy(0, Nations.ENGLAND, self.territories.SKAGERRAK, self.territories.NORWAY, self.territories.SWEDEN), Move(0, Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.NORWAY), Move(0, Nations.RUSSIA, self.territories.SWEDEN, self.territories.NORWAY), Support(0, Nations.RUSSIA, self.territories.NORWEGIAN_SEA, self.territories.SWEDEN, self.territories.NORWAY), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[5].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[4].outcome, Outcomes.FAILS) self.assertEqual(orders[6].outcome, Outcomes.SUCCEEDS) def test_two_unit_in_one_area_bug_with_double_convoy(self): """ Similar to the previous test case, but now both units move by convoy. England: F North Sea Convoys A London - Belgium A Holland Supports A London - Belgium A Yorkshire - London A London - Belgium A Ruhr Supports A London - Belgium France: F English Channel Convoys A Belgium - London A Belgium - London A Wales Supports A Belgium - London Belgium and London are swapped, while the army in Yorkshire fails to move to London. """ pieces = [ Fleet(0, Nations.ENGLAND, self.territories.NORTH_SEA), Army(0, Nations.ENGLAND, self.territories.HOLLAND), Army(0, Nations.ENGLAND, self.territories.YORKSHIRE), Army(0, Nations.ENGLAND, self.territories.LONDON), Army(0, Nations.ENGLAND, self.territories.RUHR), Fleet(0, Nations.FRANCE, self.territories.ENGLISH_CHANNEL), Army(0, Nations.FRANCE, self.territories.BELGIUM), Army(0, Nations.FRANCE, self.territories.WALES), ] orders = [ Convoy(0, Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.LONDON, self.territories.BELGIUM), Support(0, Nations.ENGLAND, self.territories.HOLLAND, self.territories.LONDON, self.territories.BELGIUM), Move(0, Nations.ENGLAND, self.territories.YORKSHIRE, self.territories.LONDON), Move(0, Nations.ENGLAND, self.territories.LONDON, self.territories.BELGIUM, via_convoy=True), Support(0, Nations.ENGLAND, self.territories.RUHR, self.territories.LONDON, self.territories.BELGIUM), Convoy(0, Nations.FRANCE, self.territories.ENGLISH_CHANNEL, self.territories.BELGIUM, self.territories.LONDON), Move(0, Nations.FRANCE, self.territories.BELGIUM, self.territories.LONDON, via_convoy=True), Support(0, Nations.FRANCE, self.territories.WALES, self.territories.BELGIUM, self.territories.LONDON), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[1].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[2].outcome, Outcomes.FAILS) self.assertEqual(orders[3].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[4].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[6].outcome, Outcomes.SUCCEEDS) self.assertEqual(orders[7].outcome, Outcomes.SUCCEEDS)
def setUp(self): self.state = State(Season.SPRING, Phase.ORDER, 1900)
class TestBounceOccured(unittest.TestCase): def setUp(self): self.state = State() self.territories = Territories() self.named_coasts = NamedCoasts(self.territories) self.state = register_all(self.state, self.territories, self.named_coasts) def test_simple_bounce(self): pieces = [ Army(0, Nations.FRANCE, self.territories.PICARDY), Army(0, Nations.GERMANY, self.territories.BURGUNDY), ] orders = [ Move(0, Nations.FRANCE, self.territories.PICARDY, self.territories.PARIS), Move(0, Nations.GERMANY, self.territories.BURGUNDY, self.territories.PARIS), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertTrue(orders[0].target.bounce_occurred) def test_three_way_bounce(self): pieces = [ Army(0, Nations.FRANCE, self.territories.PICARDY), Army(0, Nations.GERMANY, self.territories.BURGUNDY), Army(0, Nations.GERMANY, self.territories.GASCONY), ] orders = [ Move(0, Nations.FRANCE, self.territories.PICARDY, self.territories.PARIS), Move(0, Nations.GERMANY, self.territories.BURGUNDY, self.territories.PARIS), Move(0, Nations.GERMANY, self.territories.GASCONY, self.territories.PARIS), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertTrue(orders[0].target.bounce_occurred) def test_no_contest(self): pieces = [ Army(0, Nations.FRANCE, self.territories.PICARDY), ] orders = [ Move(0, Nations.FRANCE, self.territories.PICARDY, self.territories.PARIS), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertFalse(orders[0].target.bounce_occurred) def test_no_attack(self): territory = self.territories.PARIS process(self.state) self.assertFalse(territory.bounce_occurred)
class TestCircularMovement(unittest.TestCase): def setUp(self): self.state = State() self.territories = Territories() self.named_coasts = NamedCoasts(self.territories) self.state = register_all(self.state, self.territories, self.named_coasts) def test_three_army_circular_movement(self): """ Three units can change place, even in spring 1901. Turkey: F Ankara - Constantinople A Constantinople - Smyrna A Smyrna - Ankara All three units will move. """ pieces = [ Fleet(Nations.TURKEY, self.territories.ANKARA), Army(Nations.TURKEY, self.territories.CONSTANTINOPLE), Army(Nations.TURKEY, self.territories.SMYRNA) ] orders = [ Move(Nations.TURKEY, self.territories.ANKARA, self.territories.CONSTANTINOPLE), Move(Nations.TURKEY, self.territories.CONSTANTINOPLE, self.territories.SMYRNA), Move(Nations.TURKEY, self.territories.SMYRNA, self.territories.ANKARA), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].move_decision, Outcomes.MOVES) self.assertEqual(orders[1].move_decision, Outcomes.MOVES) self.assertEqual(orders[2].move_decision, Outcomes.MOVES) def test_three_army_circular_movement_with_support(self): """ Three units can change place, even when one gets support. Turkey: F Ankara - Constantinople A Constantinople - Smyrna A Smyrna - Ankara A Bulgaria Supports F Ankara - Constantinople Of course the three units will move, but knowing how programs are written, this can confuse the adjudicator. """ pieces = [ Fleet(Nations.TURKEY, self.territories.ANKARA), Army(Nations.TURKEY, self.territories.BULGARIA), Army(Nations.TURKEY, self.territories.CONSTANTINOPLE), Army(Nations.TURKEY, self.territories.SMYRNA) ] orders = [ Move(Nations.TURKEY, self.territories.ANKARA, self.territories.CONSTANTINOPLE), Move(Nations.TURKEY, self.territories.CONSTANTINOPLE, self.territories.SMYRNA), Move(Nations.TURKEY, self.territories.SMYRNA, self.territories.ANKARA), Support(Nations.TURKEY, self.territories.BULGARIA, self.territories.ANKARA, self.territories.CONSTANTINOPLE), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].move_decision, Outcomes.MOVES) self.assertEqual(orders[1].move_decision, Outcomes.MOVES) self.assertEqual(orders[2].move_decision, Outcomes.MOVES) self.assertEqual(orders[3].support_decision, Outcomes.GIVEN) def test_disrupted_three_army_circular_movement(self): """ When one of the units bounces, the whole circular movement will hold. Turkey: F Ankara - Constantinople A Constantinople - Smyrna A Smyrna - Ankara A Bulgaria - Constantinople Every unit will keep its place. """ pieces = [ Fleet(Nations.TURKEY, self.territories.ANKARA), Army(Nations.TURKEY, self.territories.BULGARIA), Army(Nations.TURKEY, self.territories.CONSTANTINOPLE), Army(Nations.TURKEY, self.territories.SMYRNA) ] orders = [ Move(Nations.TURKEY, self.territories.ANKARA, self.territories.CONSTANTINOPLE), Move(Nations.TURKEY, self.territories.CONSTANTINOPLE, self.territories.SMYRNA), Move(Nations.TURKEY, self.territories.SMYRNA, self.territories.ANKARA), Move(Nations.TURKEY, self.territories.BULGARIA, self.territories.CONSTANTINOPLE), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].move_decision, Outcomes.FAILS) self.assertEqual(orders[1].move_decision, Outcomes.FAILS) self.assertEqual(orders[2].move_decision, Outcomes.FAILS) self.assertEqual(orders[3].move_decision, Outcomes.FAILS)
class TestCoastalIssues(unittest.TestCase): def setUp(self): self.state = State() self.territories = Territories() self.named_coasts = NamedCoasts(self.territories) self.state = register_all(self.state, self.territories, self.named_coasts) def test_moving_with_unspecified_coast_when_coast_necessary(self): """ Coast is significant in this case: France: F Portugal - Spain Some adjudicators take a default coast (see issue 4.B.1). I prefer that the move fails. """ fleet = Fleet(Nations.FRANCE, self.territories.PORTUGAL) order = Move(Nations.FRANCE, self.territories.PORTUGAL, self.territories.SPAIN) self.state.register(fleet, order) with self.assertRaises(ValueError): process(self.state) def test_moving_with_unspecified_coast_when_coast_unnecessary(self): """ There is only one coast possible in this case: France: F Gascony - Spain Since the North Coast is the only coast that can be reached, it seems logical that the a move is attempted to the north coast of Spain. Some adjudicators require that a coast is also specified in this case and will decide that the move fails or take a default coast (see issue 4.B.2). I prefer that an attempt is made to the only possible coast, the north coast of Spain. """ fleet = Fleet(Nations.FRANCE, self.territories.GASCONY) order = Move(Nations.FRANCE, self.territories.GASCONY, self.territories.SPAIN) self.state.register(fleet, order) with self.assertRaises(ValueError): process(self.state) def test_moving_with_wrong_coast_when_coast_is_not_necessary(self): """ If only one coast is possible, but the wrong coast can be specified. France: F Gascony - Spain(sc) If the rules are played very clemently, a move will be attempted to the north coast of Spain. However, since this order is very clear and precise, it is more common that the move fails (see issue 4.B.3). I prefer that the move fails. """ fleet = Fleet(Nations.FRANCE, self.territories.GASCONY) order = Move(Nations.FRANCE, self.territories.GASCONY, self.territories.SPAIN, self.named_coasts.SPAIN_SC) self.state.register(fleet, order) process(self.state) self.assertEqual(order.legal_decision, Outcomes.ILLEGAL) self.assertEqual(order.illegal_message, illegal_messages.M007) def test_support_to_unreachable_coast_allowed(self): """ A fleet can give support to a coast where it can not go. France: F Gascony - Spain(nc) F Marseilles Supports F Gascony - Spain(nc) Italy: F Western Mediterranean - Spain(sc) Although the fleet in Marseilles can not go to the north coast it can still support targeting the north coast. So, the support is successful, the move of the fleet in Gascony succeeds and the move of the Italian fleet fails. """ pieces = [ Fleet(Nations.FRANCE, self.territories.GASCONY), Fleet(Nations.FRANCE, self.territories.MARSEILLES), Fleet(Nations.ITALY, self.territories.WESTERN_MEDITERRANEAN) ] fleet_gascony_move = Move(Nations.FRANCE, self.territories.GASCONY, self.territories.SPAIN, self.named_coasts.SPAIN_NC) fleet_marseilles_support = Support(Nations.FRANCE, self.territories.MARSEILLES, self.territories.GASCONY, self.territories.SPAIN) fleet_western_med_move = Move(Nations.ITALY, self.territories.WESTERN_MEDITERRANEAN, self.territories.SPAIN, self.named_coasts.SPAIN_SC) self.state.register(*pieces, fleet_gascony_move, fleet_marseilles_support, fleet_western_med_move) self.state.post_register_updates() process(self.state) self.assertEqual(fleet_gascony_move.move_decision, Outcomes.MOVES) self.assertEqual(fleet_western_med_move.move_decision, Outcomes.FAILS) self.assertEqual(fleet_marseilles_support.support_decision, Outcomes.GIVEN) 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_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(Nations.ENGLAND, self.territories.NORTH_ATLANTIC), Fleet(Nations.ENGLAND, self.territories.IRISH_SEA), Fleet(Nations.FRANCE, self.territories.SPAIN, self.named_coasts.SPAIN_NC), Fleet(Nations.FRANCE, self.territories.MID_ATLANTIC), Fleet(Nations.ITALY, self.territories.GULF_OF_LYON), ] orders = [ Move(Nations.ENGLAND, self.territories.NORTH_ATLANTIC, self.territories.MID_ATLANTIC), Support(Nations.ENGLAND, self.territories.IRISH_SEA, self.territories.NORTH_ATLANTIC, self.territories.MID_ATLANTIC), Support(Nations.FRANCE, self.territories.SPAIN, self.territories.MID_ATLANTIC, self.territories.MID_ATLANTIC), Hold(Nations.FRANCE, self.territories.MID_ATLANTIC), Move(Nations.ITALY, self.territories.GULF_OF_LYON, self.territories.SPAIN, self.named_coasts.SPAIN_SC), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].move_decision, Outcomes.MOVES) self.assertEqual(orders[1].support_decision, Outcomes.GIVEN) self.assertEqual(orders[2].support_decision, Outcomes.CUT) self.assertEqual(orders[4].move_decision, Outcomes.FAILS) self.assertEqual(pieces[3].dislodged_decision, Outcomes.DISLODGED) def test_supporting_with_unspecified_coast(self): """ Most house rules accept support orders without coast specification. France: F Portugal Supports F Mid-Atlantic Ocean - Spain F Mid-Atlantic Ocean - Spain(nc) Italy: F Gulf of Lyon Supports F Western Mediterranean - Spain(sc) F Western Mediterranean - Spain(sc) See issue 4.B.4. If coasts are not required in support orders, then the support of Portugal is successful. This means that the Italian fleet in the Western Mediterranean bounces. Some adjudicators may not accept a support order without coast (the support will fail or a default coast is taken). In that case the support order of Portugal fails (in case of a default coast the coast will probably the south coast) and the Italian fleet in the Western Mediterranean will successfully move. I prefer that the support succeeds and the Italian fleet in the Western Mediterranean bounces. """ pieces = [ Fleet(Nations.FRANCE, self.territories.PORTUGAL), Fleet(Nations.FRANCE, self.territories.MID_ATLANTIC), Fleet(Nations.ITALY, self.territories.GULF_OF_LYON), Fleet(Nations.ITALY, self.territories.WESTERN_MEDITERRANEAN), ] orders = [ Support(Nations.FRANCE, self.territories.PORTUGAL, self.territories.MID_ATLANTIC, self.territories.SPAIN), Move(Nations.FRANCE, self.territories.MID_ATLANTIC, self.territories.SPAIN, self.named_coasts.SPAIN_NC), Support(Nations.ITALY, self.territories.GULF_OF_LYON, self.territories.WESTERN_MEDITERRANEAN, self.territories.SPAIN), Move(Nations.ITALY, self.territories.WESTERN_MEDITERRANEAN, self.territories.SPAIN, self.named_coasts.SPAIN_SC), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].support_decision, Outcomes.GIVEN) self.assertEqual(orders[1].move_decision, Outcomes.FAILS) self.assertEqual(orders[2].support_decision, Outcomes.GIVEN) self.assertEqual(orders[3].move_decision, Outcomes.FAILS) def test_supporting_with_unspecified_coast_when_only_one_coast_is_possible(self): """ Some hardliners require a coast in a support order even when only one coast is possible. France: F Portugal Supports F Gascony - Spain F Gascony - Spain(nc) Italy: F Gulf of Lyon Supports F Western Mediterranean - Spain(sc) F Western Mediterranean - Spain(sc) See issue 4.B.4. If coasts are not required in support orders, then the support of Portugal is successful. This means that the Italian fleet in the Western Mediterranean bounces. Some adjudicators may not accept a support order without coast (the support will fail or a default coast is taken). In that case the support order of Portugal fails (in case of a default coast the coast will probably the south coast) and the Italian fleet in the Western Mediterranean will successfully move. I prefer that supporting without coasts should be allowed. So I prefer that the support of Portugal is successful and that the Italian fleet in the Western Mediterranean bounces. """ pieces = [ Fleet(Nations.FRANCE, self.territories.PORTUGAL), Fleet(Nations.FRANCE, self.territories.GASCONY), Fleet(Nations.ITALY, self.territories.GULF_OF_LYON), Fleet(Nations.ITALY, self.territories.WESTERN_MEDITERRANEAN), ] orders = [ Support(Nations.FRANCE, self.territories.PORTUGAL, self.territories.GASCONY, self.territories.SPAIN), Move(Nations.FRANCE, self.territories.GASCONY, self.territories.SPAIN, self.named_coasts.SPAIN_NC), Support(Nations.ITALY, self.territories.GULF_OF_LYON, self.territories.WESTERN_MEDITERRANEAN, self.territories.SPAIN), Move(Nations.ITALY, self.territories.WESTERN_MEDITERRANEAN, self.territories.SPAIN, self.named_coasts.SPAIN_SC), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].support_decision, Outcomes.GIVEN) self.assertEqual(orders[1].move_decision, Outcomes.FAILS) self.assertEqual(orders[2].support_decision, Outcomes.GIVEN) self.assertEqual(orders[3].move_decision, Outcomes.FAILS) def test_coast_cannot_be_ordered_to_change(self): """ The coast can not change by just ordering the other coast. France has a fleet on the north coast of Spain and orders: France: F Spain(sc) - Gulf of Lyon The move fails. """ # Not really relevant because can't specify source coast fleet = Fleet(Nations.FRANCE, self.territories.SPAIN, self.named_coasts.SPAIN_NC) move = Move(Nations.FRANCE, self.territories.SPAIN, self.territories.SPAIN, self.named_coasts.SPAIN_SC) self.state.register(fleet, move) self.state.post_register_updates() process(self.state) self.assertEqual(move.legal_decision, Outcomes.ILLEGAL) self.assertEqual(move.illegal_message, illegal_messages.M002) def test_army_movement_with_coastal_specification(self): """ For armies the coasts are irrelevant: France: A Gascony - Spain(nc) If only perfect orders are accepted, then the move will fail. But it is also possible that coasts are ignored in this case and a move will be attempted (see issue 4.B.6). I prefer that a move will be attempted. """ army = Army(Nations.FRANCE, self.territories.GASCONY) move = Move(Nations.FRANCE, self.territories.GASCONY, self.territories.SPAIN, self.named_coasts.SPAIN_NC) self.state.register(army, move) self.state.post_register_updates() process(self.state) self.assertEqual(move.move_decision, Outcomes.MOVES) self.assertEqual(move.legal_decision, Outcomes.LEGAL) def test_coastal_crawl_not_allowed(self): """ If a fleet is leaving a sector from a certain coast while in the opposite direction another fleet is moving to another coast of the sector, it is still a head to head battle. This has been decided in the great revision of the 1961 rules that resulted in the 1971 rules. Turkey: F Bulgaria(sc) - Constantinople F Constantinople - Bulgaria(ec) Both moves fail. """ pieces = [ Fleet(Nations.TURKEY, self.territories.BULGARIA, self.named_coasts.BULGARIA_SC), Fleet(Nations.TURKEY, self.territories.CONSTANTINOPLE), ] orders = [ Move(Nations.TURKEY, self.territories.BULGARIA, self.territories.CONSTANTINOPLE), Move(Nations.TURKEY, self.territories.CONSTANTINOPLE, self.territories.BULGARIA, self.named_coasts.BULGARIA_EC), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertEqual(orders[0].move_decision, Outcomes.FAILS) self.assertEqual(orders[1].move_decision, Outcomes.FAILS) def test_building_with_unspecified_coast(self): """ Coast must be specified in certain build cases: Russia: Build F St Petersburg If no default coast is taken (see issue 4.B.7), the build fails. I do not like default coast, so I prefer that the build fails. """ order = Build(Nations.RUSSIA, self.territories.ST_PETERSBURG, 'FLEET') self.state.register(order) process(self.state) self.assertEqual(order.legal_decision, Outcomes.ILLEGAL) self.assertEqual(order.illegal_message, illegal_messages.B006)
class TestBasicChecks(unittest.TestCase): def setUp(self): self.state = State() self.territories = Territories() self.state = register_all(self.state, self.territories, []) def test_moving_to_an_area_that_is_not_a_neighbour(self): """ Check if an illegal move (without convoy) will fail. England: F North Sea - Picardy Order should fail. """ fleet = Fleet(0, Nations.ENGLAND, self.territories.NORTH_SEA) order = Move(0, Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.PICARDY) self.state.register(fleet, order) process(self.state) self.assertTrue(order.illegal) self.assertEqual(order.outcome, Outcomes.FAILS) self.assertEqual(order.illegal_code, '004') self.assertEqual( order.illegal_verbose, 'Fleet cannot reach non-adjacent territory.' ) def test_move_army_to_sea(self): """ Check if an army could not be moved to open sea. England: A Liverpool - Irish Sea Order should fail. """ army = Army(0, Nations.ENGLAND, self.territories.LIVERPOOL) order = Move(0, Nations.ENGLAND, self.territories.LIVERPOOL, self.territories.IRISH_SEA) self.state.register(army, order) process(self.state) self.assertTrue(order.illegal) self.assertEqual(order.outcome, Outcomes.FAILS) self.assertEqual(order.illegal_code, '005') self.assertEqual( order.illegal_verbose, 'Army cannot enter a sea territory' ) def test_move_fleet_to_land(self): """ Check whether a fleet can not move to land. Germany: F Kiel - Munich Order should fail. """ fleet = Fleet(0, Nations.GERMANY, self.territories.KIEL) order = Move(0, Nations.GERMANY, self.territories.KIEL, self.territories.MUNICH) self.state.register(fleet, order) process(self.state) self.assertTrue(order.illegal) self.assertEqual(order.outcome, Outcomes.FAILS) self.assertEqual( order.illegal_code, '006' ) self.assertEqual( order.illegal_verbose, 'Fleet cannot enter an inland territory', ) def test_move_to_own_sector(self): """ Moving to the same sector is an illegal move (2000 rulebook, page 4, "An Army can be ordered to move into an adjacent inland or coastal province."). Germany: F Kiel - Kiel Program should not crash. """ army = Army(0, Nations.GERMANY, self.territories.KIEL) order = Move(0, Nations.GERMANY, self.territories.KIEL, self.territories.KIEL) self.state.register(army, order) process(self.state) self.assertTrue(order.illegal) self.assertEqual(order.illegal_code, '002') self.assertEqual( order.illegal_verbose, 'Source and target cannot be the same territory.' ) # TODO more checks here def test_move_to_own_sector_with_convoy(self): """ Moving to the same sector is still illegal with convoy (2000 rulebook, page 4, "Note: An Army can move across water provinces from one coastal province to another..."). England: F North Sea Convoys A Yorkshire - Yorkshire A Yorkshire - Yorkshire A Liverpool Supports A Yorkshire - Yorkshire Germany: F London - Yorkshire A Wales Supports F London - Yorkshire The move of the army in Yorkshire is illegal. This makes the support of Liverpool also illegal and without the support, the Germans have a stronger force. The fleet in London dislodges the army in Yorkshire. """ pieces = [ Fleet(0, Nations.ENGLAND, self.territories.NORTH_SEA), Army(0, Nations.ENGLAND, self.territories.YORKSHIRE), Army(0, Nations.ENGLAND, self.territories.LIVERPOOL), Fleet(0, Nations.ENGLAND, self.territories.LONDON), Army(0, Nations.ENGLAND, self.territories.WALES), ] fleet_north_sea_convoy = Convoy(0, Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.YORKSHIRE, self.territories.YORKSHIRE) army_yorkshire_move = Move(0, Nations.ENGLAND, self.territories.YORKSHIRE, self.territories.YORKSHIRE) army_liverpool_support = Support(0, Nations.ENGLAND, self.territories.LIVERPOOL, self.territories.YORKSHIRE, self.territories.YORKSHIRE) fleet_london_move = Move(0, Nations.GERMANY, self.territories.LONDON, self.territories.YORKSHIRE) army_wales_support = Support(0, Nations.GERMANY, self.territories.WALES, self.territories.LONDON, self.territories.YORKSHIRE) self.state.register( *pieces, fleet_north_sea_convoy, army_yorkshire_move, army_liverpool_support, fleet_london_move, army_wales_support) self.state.post_register_updates() process(self.state) self.assertTrue(army_yorkshire_move.illegal) self.assertEqual(army_yorkshire_move.outcome, Outcomes.FAILS) self.assertEqual(army_yorkshire_move.illegal_code, '002') self.assertEqual( army_yorkshire_move.illegal_verbose, 'Source and target cannot be the same territory.' ) 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 = Fleet(0, Nations.ENGLAND, self.territories.LONDON) order = Move(0, Nations.GERMANY, self.territories.LONDON, self.territories.NORTH_SEA) self.state.register(fleet, order) 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 test_only_armies_can_be_convoyed(self): """ A fleet can not be convoyed. England: F London - Belgium F North Sea Convoys A London - Belgium Move from London to Belgium should fail. """ pieces = [ Fleet(0, Nations.ENGLAND, self.territories.LONDON), Fleet(0, Nations.ENGLAND, self.territories.NORTH_SEA), ] fleet_london_move = Move(0, Nations.ENGLAND, self.territories.LONDON, self.territories.BELGIUM, via_convoy=True) fleet_north_sea_convoy = Convoy(0, Nations.ENGLAND, self.territories.NORTH_SEA, self.territories.LONDON, self.territories.BELGIUM) self.state.register(*pieces, fleet_london_move, fleet_north_sea_convoy) self.state.post_register_updates() process(self.state) self.assertTrue(fleet_london_move.illegal) self.assertTrue(fleet_london_move.outcome == Outcomes.FAILS) self.assertEqual(fleet_london_move.illegal_code, '004') self.assertEqual( fleet_london_move.illegal_verbose, 'Fleet cannot reach non-adjacent territory.' ) self.assertTrue(fleet_north_sea_convoy.illegal) self.assertEqual( fleet_north_sea_convoy.illegal_verbose, 'Cannot convoy a fleet.' ) def test_support_to_hold_yourself_not_possible(self): """ An army can not get an additional hold power by supporting itself. Italy: A Venice - Trieste A Tyrolia Supports A Venice - Trieste Austria: F Trieste Supports F Trieste The fleet in Trieste should be dislodged. """ pieces = [ Army(0, Nations.ITALY, self.territories.VENICE), Army(0, Nations.ITALY, self.territories.TYROLIA), Fleet(0, Nations.AUSTRIA, self.territories.TRIESTE) ] army_venice_move = Move(0, Nations.ITALY, self.territories.VENICE, self.territories.TRIESTE) army_tyrolia_support = Support(0, Nations.ITALY, self.territories.TYROLIA, self.territories.VENICE, self.territories.TRIESTE) fleet_trieste_support = Support(0, Nations.AUSTRIA, self.territories.TRIESTE, self.territories.TRIESTE, self.territories.TRIESTE) self.state.register(*pieces, army_venice_move, army_tyrolia_support, fleet_trieste_support) process(self.state) self.assertTrue(fleet_trieste_support.illegal) self.assertEqual( fleet_trieste_support.illegal_verbose, 'Source and target cannot be the same territory.' ) def test_fleet_must_follow_coast_if_not_on_sea(self): """ If two places are adjacent, that does not mean that a fleet can move between those two places. An implementation that only holds one list of adjacent places for each place, is incorrect. Italy: F Rome - Venice Move fails. An army can go from Rome to Venice, but a fleet can not. """ fleet = Fleet(0, Nations.ITALY, self.territories.ROME, self.territories.VENICE) order = Move(0, Nations.ITALY, self.territories.ROME, self.territories.VENICE) self.state.register(fleet, order) process(self.state) self.assertTrue(order.illegal) self.assertEqual(order.illegal_code, '007') self.assertEqual( order.illegal_verbose, 'Fleet cannot reach coastal territory without shared coastline.' ) 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(0, Nations.AUSTRIA, self.territories.VENICE), Fleet(0, Nations.ITALY, self.territories.ROME), Army(0, Nations.ITALY, self.territories.APULIA) ] # TODO finish army_austria_hold = Hold(0, Nations.AUSTRIA, self.territories.VENICE) fleet_rome_support = Support(0, Nations.ITALY, self.territories.ROME, self.territories.APULIA, self.territories.VENICE) army_apulia_move = Move(0, 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.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_simple_bounce(self): """ Two armies bouncing on each other. Austria: A Vienna - Tyrolia Italy: A Venice - Tyrolia The two units bounce. """ pieces = [ Army(0, Nations.AUSTRIA, self.territories.VIENNA), Army(0, Nations.ITALY, self.territories.VENICE), ] army_vienna_move = Move(0, Nations.AUSTRIA, self.territories.VIENNA, self.territories.TYROLIA) army_venice_move = Move(0, Nations.ITALY, self.territories.VENICE, self.territories.TYROLIA) self.state.register(*pieces, army_venice_move, army_vienna_move) self.state.post_register_updates() process(self.state) self.assertTrue(army_venice_move.legal) self.assertTrue(army_vienna_move.legal) self.assertEqual(army_vienna_move.outcome, Outcomes.FAILS) self.assertEqual(army_venice_move.outcome, Outcomes.FAILS) # TODO # self.assertFalse(army_vienna.dislodged) # self.assertFalse(army_venice.dislodged) def test_bounce_of_three_units(self): """ If three units move to the same place, the adjudicator should not bounce the first two units and then let the third unit go to the now open place. Austria: A Vienna - Tyrolia Germany: A Munich - Tyrolia Italy: A Venice - Tyrolia The three units bounce. """ pieces = [ Army(0, Nations.AUSTRIA, self.territories.VIENNA), Army(0, Nations.ITALY, self.territories.VENICE), Army(0, Nations.GERMANY, self.territories.MUNICH) ] army_vienna_move = Move(0, Nations.AUSTRIA, self.territories.VIENNA, self.territories.TYROLIA) army_venice_move = Move(0, Nations.ITALY, self.territories.VENICE, self.territories.TYROLIA) army_munich_move = Move(0, Nations.GERMANY, self.territories.MUNICH, self.territories.TYROLIA) self.state.register(*pieces, army_venice_move, army_vienna_move, army_munich_move) process(self.state) self.assertTrue(army_venice_move.legal) self.assertTrue(army_vienna_move.legal) self.assertTrue(army_munich_move.legal) self.assertEqual(army_vienna_move.outcome, Outcomes.FAILS) self.assertEqual(army_venice_move.outcome, Outcomes.FAILS) self.assertEqual(army_munich_move.outcome, Outcomes.FAILS)
def setUp(self): self.state = State()
class TestRetreating(unittest.TestCase): def setUp(self): self.state = State() self.territories = Territories() self.named_coasts = NamedCoasts(self.territories) self.state = register_all(self.state, self.territories, self.named_coasts) def test_unit_may_not_retreat_from_area_from_which_it_was_attacked(self): """ Well, that would be of course stupid. Still, the adjudicator must be tested on this. """ pieces = [ Army(0, Nations.ENGLAND, self.territories.NORWAY, attacker_territory=self.territories.SWEDEN), ] orders = [ Retreat(0, Nations.ENGLAND, self.territories.NORWAY, self.territories.SWEDEN), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertTrue(orders[0].illegal) self.assertEqual( orders[0].illegal_verbose, 'Piece cannot retreat to the territory from which it was attacked.' ) self.assertEqual(orders[0].outcome, Outcomes.FAILS) def test_unit_may_not_retreat_to_contested_area(self): """ Stand off prevents retreat to the area. """ self.territories.SWEDEN.contested = True pieces = [ Army(0, Nations.ENGLAND, self.territories.NORWAY, attacker_territory=self.territories.FINLAND), ] orders = [ Retreat(0, Nations.ENGLAND, self.territories.NORWAY, self.territories.SWEDEN), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertTrue(orders[0].illegal) self.assertEqual( orders[0].illegal_verbose, ('Cannot retreat to a territory which was contested on the ' 'previous turn.')) self.assertEqual(orders[0].outcome, Outcomes.FAILS) def test_multiple_retreat_to_the_same_area_causes_disband(self): """ There can only be one unit in an area. """ pieces = [ Army(0, Nations.ENGLAND, self.territories.NORWAY, attacker_territory=self.territories.ST_PETERSBURG), Army(0, Nations.ENGLAND, self.territories.FINLAND, attacker_territory=self.territories.ST_PETERSBURG), ] orders = [ Retreat(0, Nations.ENGLAND, self.territories.NORWAY, self.territories.SWEDEN), Retreat(0, Nations.ENGLAND, self.territories.FINLAND, self.territories.SWEDEN), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertTrue(orders[0].legal) self.assertEqual(orders[0].outcome, Outcomes.FAILS) self.assertTrue(orders[1].legal) self.assertEqual(orders[1].outcome, Outcomes.FAILS) def test_three_retreats_to_the_same_area_causes_disband(self): """ When three units retreat to the same area, then all three units are disbanded. """ pieces = [ Army(0, Nations.ENGLAND, self.territories.NORWAY, attacker_territory=self.territories.ST_PETERSBURG), Army(0, Nations.ENGLAND, self.territories.FINLAND, attacker_territory=self.territories.ST_PETERSBURG), Army(0, Nations.RUSSIA, self.territories.DENMARK, attacker_territory=self.territories.KIEL), ] orders = [ Retreat(0, Nations.ENGLAND, self.territories.NORWAY, self.territories.SWEDEN), Retreat(0, Nations.ENGLAND, self.territories.FINLAND, self.territories.SWEDEN), Retreat(0, Nations.RUSSIA, self.territories.DENMARK, self.territories.SWEDEN), ] self.state.register(*pieces, *orders) self.state.post_register_updates() process(self.state) self.assertTrue(orders[0].legal) self.assertEqual(orders[0].outcome, Outcomes.FAILS) self.assertTrue(orders[1].legal) self.assertEqual(orders[1].outcome, Outcomes.FAILS) self.assertTrue(orders[2].legal) self.assertEqual(orders[2].outcome, Outcomes.FAILS)
def setUp(self): self.state = State() self.territories = Territories() self.named_coasts = NamedCoasts(self.territories) self.state = register_all(self.state, self.territories, self.named_coasts)
def process_game_state(data): territory_map = {} named_coast_map = {} # Marshall data into expected format and validate validated_data = TurnSchema().load(data) season = validated_data['season'] phase = validated_data['phase'] year = validated_data['year'] # Instantiate `State` for this turn state = State(season, phase, year) # Initialise territory instances and register each to state. Add to # territory map for territory_data in validated_data['territories']: territory_type = territory_data.pop('type') territory_class = territory_type_dict[territory_type] territory = territory_class(state, **territory_data) territory_map[territory_data['id']] = territory # Initialise named coasts - grab parent from territory map for named_coast_data in validated_data['named_coasts']: named_coast_data['parent'] = territory_map[named_coast_data['parent']] named_coast = NamedCoast(state, **named_coast_data) named_coast_map[named_coast.id] = named_coast # Initialise orders - grab source, target, aux, target_coast from maps for order_data in validated_data['orders']: order_type = order_data.pop('type') order_class = order_type_dict[order_type] for arg_name in ['source', 'target', 'aux']: territory_id = order_data[arg_name] if territory_id: order_data[arg_name] = territory_map[territory_id] target_coast_id = order_data['target_coast'] if target_coast_id: order_data['target_coast'] = named_coast_map[target_coast_id] order_class(state, **order_data) # Initialise pieces - grab source, target, aux, target_coast from maps for piece_data in validated_data['pieces']: piece_type = piece_data.pop('type') piece_class = piece_type_dict[piece_type] for arg_name in ['territory', 'attacker_territory']: territory_id = piece_data[arg_name] if territory_id: piece_data[arg_name] = territory_map[territory_id] named_coast_id = piece_data['named_coast'] if named_coast_id: piece_data['named_coast'] = named_coast_map[named_coast_id] piece_class(state, **piece_data) # Initialise nation instances and register each to state for nation_data in validated_data['nations']: Nation(state, **nation_data) # Process game state process(state) # Serialize processed game state and return processed_data = TurnSchema().dump(state) return processed_data