547 lines
19 KiB
Python
547 lines
19 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""Comprehensive test suite for the monop-irc parser.
|
||
|
|
|
||
|
|
Covers all game states derived from the monop-irc C source:
|
||
|
|
- execute.c: rolls, movement, doubles, passing Go
|
||
|
|
- cards.c: Chance/CC cards (money, movement, GOJF, tax/repair)
|
||
|
|
- spec.c: income tax, luxury tax, go-to-jail square
|
||
|
|
- jail.c: jail entry, doubles out, pay out, GOJF card out, 3rd turn forced out
|
||
|
|
- rent.c: property rent, railroad rent, utility rent, monopoly double rent, houses/hotel
|
||
|
|
- houses.c: buying/selling houses
|
||
|
|
- morg.c: mortgage/unmortgage
|
||
|
|
- trade.c: trading properties
|
||
|
|
- prop.c: buying properties, bidding
|
||
|
|
- print.c: holdings, board display
|
||
|
|
- misc.c: bankruptcy
|
||
|
|
"""
|
||
|
|
|
||
|
|
import os
|
||
|
|
import sys
|
||
|
|
import json
|
||
|
|
import unittest
|
||
|
|
|
||
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'bot'))
|
||
|
|
from parser import MonopParser
|
||
|
|
|
||
|
|
|
||
|
|
class TestParserSetup(unittest.TestCase):
|
||
|
|
def setUp(self):
|
||
|
|
self.p = MonopParser()
|
||
|
|
self.p.add_player("Alice", 1)
|
||
|
|
self.p.add_player("Bob", 2)
|
||
|
|
self.p.add_player("Charlie", 3)
|
||
|
|
|
||
|
|
def state(self):
|
||
|
|
return self.p.get_state()
|
||
|
|
|
||
|
|
def alice(self):
|
||
|
|
return self.state()["players"][0]
|
||
|
|
|
||
|
|
def bob(self):
|
||
|
|
return self.state()["players"][1]
|
||
|
|
|
||
|
|
def charlie(self):
|
||
|
|
return self.state()["players"][2]
|
||
|
|
|
||
|
|
|
||
|
|
class TestRollAndMovement(TestParserSetup):
|
||
|
|
"""Tests from execute.c: do_move(), move(), show_move()"""
|
||
|
|
|
||
|
|
def test_basic_roll(self):
|
||
|
|
self.p.parse_line("roll is 3, 4")
|
||
|
|
log = self.state()["log"]
|
||
|
|
self.assertTrue(any("roll is 3, 4" in e["text"] for e in log))
|
||
|
|
|
||
|
|
def test_movement_to_property(self):
|
||
|
|
self.p.parse_line("That puts you on Oriental ave. (L)")
|
||
|
|
self.assertEqual(self.alice()["location"], 6)
|
||
|
|
|
||
|
|
def test_movement_to_railroad(self):
|
||
|
|
self.p.parse_line("That puts you on Reading RR")
|
||
|
|
self.assertEqual(self.alice()["location"], 5)
|
||
|
|
|
||
|
|
def test_movement_to_utility(self):
|
||
|
|
self.p.parse_line("That puts you on Electric Company")
|
||
|
|
self.assertEqual(self.alice()["location"], 12)
|
||
|
|
|
||
|
|
def test_pass_go(self):
|
||
|
|
self.p.parse_line("You pass === GO === and get $200")
|
||
|
|
self.assertEqual(self.alice()["money"], 1700)
|
||
|
|
|
||
|
|
def test_doubles_message(self):
|
||
|
|
self.p.parse_line("Alice rolled doubles. Goes again")
|
||
|
|
log = self.state()["log"]
|
||
|
|
self.assertTrue(any("rolled doubles" in e["text"] for e in log))
|
||
|
|
|
||
|
|
def test_three_doubles_jail(self):
|
||
|
|
self.p.parse_line("That's 3 doubles. You go to jail")
|
||
|
|
self.assertEqual(self.alice()["location"], 10)
|
||
|
|
self.assertTrue(self.alice()["inJail"])
|
||
|
|
|
||
|
|
def test_safe_place(self):
|
||
|
|
# Should not crash or change state meaningfully
|
||
|
|
changed = self.p.parse_line("That is a safe place")
|
||
|
|
self.assertFalse(changed)
|
||
|
|
|
||
|
|
def test_movement_to_community_chest(self):
|
||
|
|
self.p.parse_line("That puts you on Community Chest i")
|
||
|
|
# Community Chest squares: 2, 17, 33
|
||
|
|
self.assertIn(self.alice()["location"], [2, 17, 33])
|
||
|
|
|
||
|
|
def test_movement_to_chance(self):
|
||
|
|
self.p.parse_line("That puts you on Chance i")
|
||
|
|
self.assertIn(self.alice()["location"], [7, 22, 36])
|
||
|
|
|
||
|
|
def test_movement_to_just_visiting(self):
|
||
|
|
self.p.parse_line("That puts you on Just Visiting")
|
||
|
|
self.assertEqual(self.alice()["location"], 10)
|
||
|
|
|
||
|
|
def test_movement_to_free_parking(self):
|
||
|
|
self.p.parse_line("That puts you on Free Parking")
|
||
|
|
self.assertEqual(self.alice()["location"], 20)
|
||
|
|
|
||
|
|
def test_movement_to_go(self):
|
||
|
|
self.p.parse_line("That puts you on === GO ===")
|
||
|
|
self.assertEqual(self.alice()["location"], 0)
|
||
|
|
|
||
|
|
|
||
|
|
class TestPropertyPurchase(TestParserSetup):
|
||
|
|
"""Tests from prop.c: buy(), bid()"""
|
||
|
|
|
||
|
|
def test_property_cost_display(self):
|
||
|
|
self.p.parse_line("That would cost $100")
|
||
|
|
# Should not crash; just informational
|
||
|
|
|
||
|
|
def test_buy_prompt(self):
|
||
|
|
self.p.parse_line("Do you want to buy?")
|
||
|
|
# Should not crash
|
||
|
|
|
||
|
|
def test_you_own_it(self):
|
||
|
|
changed = self.p.parse_line("You own it.")
|
||
|
|
self.assertFalse(changed)
|
||
|
|
|
||
|
|
def test_process_buy(self):
|
||
|
|
"""Direct purchase tracking."""
|
||
|
|
self.p.players[0]["location"] = 6 # Oriental Ave
|
||
|
|
self.p.process_buy(0, 6)
|
||
|
|
self.assertEqual(self.alice()["money"], 1400)
|
||
|
|
self.assertIn(6, self.alice()["properties"])
|
||
|
|
self.assertEqual(self.state()["squares"][6]["owner"], 0)
|
||
|
|
|
||
|
|
def test_process_buy_railroad(self):
|
||
|
|
self.p.players[0]["location"] = 5 # Reading RR
|
||
|
|
self.p.process_buy(0, 5)
|
||
|
|
self.assertEqual(self.alice()["numRailroads"], 1)
|
||
|
|
self.assertIn(5, self.alice()["properties"])
|
||
|
|
|
||
|
|
def test_process_buy_utility(self):
|
||
|
|
self.p.players[0]["location"] = 12 # Electric Co
|
||
|
|
self.p.process_buy(0, 12)
|
||
|
|
self.assertEqual(self.alice()["numUtilities"], 1)
|
||
|
|
|
||
|
|
|
||
|
|
class TestRent(TestParserSetup):
|
||
|
|
"""Tests from rent.c: rent() for property, railroad, utility"""
|
||
|
|
|
||
|
|
def test_basic_rent(self):
|
||
|
|
# Set up: Bob owns Oriental Ave, Alice lands on it
|
||
|
|
self.p.squares[6]["owner"] = 1
|
||
|
|
self.p.players[0]["location"] = 6
|
||
|
|
self.p.parse_line("Owned by Bob")
|
||
|
|
self.p.parse_line("rent is 6")
|
||
|
|
self.assertEqual(self.alice()["money"], 1494)
|
||
|
|
self.assertEqual(self.bob()["money"], 1506)
|
||
|
|
|
||
|
|
def test_rent_with_houses(self):
|
||
|
|
self.p.squares[6]["owner"] = 1
|
||
|
|
self.p.players[0]["location"] = 6
|
||
|
|
self.p.parse_line("with 3 houses, rent is 270")
|
||
|
|
self.assertEqual(self.alice()["money"], 1230)
|
||
|
|
self.assertEqual(self.bob()["money"], 1770)
|
||
|
|
|
||
|
|
def test_rent_with_hotel(self):
|
||
|
|
self.p.squares[6]["owner"] = 1
|
||
|
|
self.p.players[0]["location"] = 6
|
||
|
|
self.p.parse_line("with a hotel, rent is 550")
|
||
|
|
self.assertEqual(self.alice()["money"], 950)
|
||
|
|
self.assertEqual(self.bob()["money"], 2050)
|
||
|
|
|
||
|
|
def test_utility_rent_single(self):
|
||
|
|
self.p.squares[12]["owner"] = 1
|
||
|
|
self.p.players[0]["location"] = 12
|
||
|
|
self.p.parse_line("rent is 4 * roll (7) = 28")
|
||
|
|
self.assertEqual(self.alice()["money"], 1472)
|
||
|
|
self.assertEqual(self.bob()["money"], 1528)
|
||
|
|
|
||
|
|
def test_utility_rent_both(self):
|
||
|
|
self.p.squares[12]["owner"] = 1
|
||
|
|
self.p.players[0]["location"] = 12
|
||
|
|
self.p.parse_line("rent is 10 * roll (8) = 80")
|
||
|
|
self.assertEqual(self.alice()["money"], 1420)
|
||
|
|
self.assertEqual(self.bob()["money"], 1580)
|
||
|
|
|
||
|
|
def test_mortgaged_property_no_rent(self):
|
||
|
|
# "The thing is mortgaged." -> lucky() -> no rent
|
||
|
|
self.p.squares[6]["owner"] = 1
|
||
|
|
self.p.squares[6]["mortgaged"] = True
|
||
|
|
self.p.players[0]["location"] = 6
|
||
|
|
# No rent line should follow
|
||
|
|
self.assertEqual(self.alice()["money"], 1500)
|
||
|
|
|
||
|
|
|
||
|
|
class TestTax(TestParserSetup):
|
||
|
|
"""Tests from spec.c: inc_tax(), lux_tax()"""
|
||
|
|
|
||
|
|
def test_luxury_tax(self):
|
||
|
|
self.p.parse_line("You lose $75")
|
||
|
|
self.assertEqual(self.alice()["money"], 1425)
|
||
|
|
|
||
|
|
def test_income_tax_200(self):
|
||
|
|
# "You were worth $1500" then pays $200
|
||
|
|
self.p.parse_line("You pay $200")
|
||
|
|
self.assertEqual(self.alice()["money"], 1300)
|
||
|
|
|
||
|
|
def test_income_tax_percentage(self):
|
||
|
|
self.p.parse_line("You were worth $1500, so you pay $150")
|
||
|
|
# The parser tracks the "You pay" pattern
|
||
|
|
# This is informational; actual deduction comes via cash tracking
|
||
|
|
|
||
|
|
def test_income_tax_prompt(self):
|
||
|
|
# The game asks: "Do you wish to lose 10% of your total worth or $200?"
|
||
|
|
# Valid answers: "10%", "ten percent", "%", "$200", "200"
|
||
|
|
# This is handled by monop_server, not the parser
|
||
|
|
pass
|
||
|
|
|
||
|
|
|
||
|
|
class TestJail(TestParserSetup):
|
||
|
|
"""Tests from jail.c and execute.c jail-related output"""
|
||
|
|
|
||
|
|
def test_go_to_jail_from_square(self):
|
||
|
|
self.p.parse_line("That puts you on Go to Jail")
|
||
|
|
# The actual jail movement happens via card/square logic
|
||
|
|
# But the GO DIRECTLY TO JAIL card message:
|
||
|
|
self.p.parse_line("Go directly to Jail")
|
||
|
|
self.assertTrue(self.alice()["inJail"])
|
||
|
|
self.assertEqual(self.alice()["location"], 10)
|
||
|
|
|
||
|
|
def test_go_directly_to_jail_card(self):
|
||
|
|
self.p.parse_line(">> GO DIRECTLY TO JAIL <<")
|
||
|
|
# Parser should detect jail from this
|
||
|
|
self.assertTrue(self.alice()["inJail"])
|
||
|
|
|
||
|
|
def test_doubles_out_of_jail(self):
|
||
|
|
self.p.players[0]["inJail"] = True
|
||
|
|
self.p.players[0]["location"] = 10
|
||
|
|
self.p.parse_line("Double roll gets you out.")
|
||
|
|
self.assertFalse(self.alice()["inJail"])
|
||
|
|
|
||
|
|
def test_sorry_doesnt_get_out(self):
|
||
|
|
self.p.players[0]["in_jail"] = True
|
||
|
|
self.p.parse_line("Sorry, that doesn't get you out")
|
||
|
|
self.assertTrue(self.alice()["inJail"])
|
||
|
|
|
||
|
|
def test_third_turn_forced_out(self):
|
||
|
|
self.p.players[0]["inJail"] = True
|
||
|
|
self.p.parse_line("It's your third turn and you didn't roll doubles. You have to pay $50")
|
||
|
|
self.assertFalse(self.alice()["inJail"])
|
||
|
|
self.assertEqual(self.alice()["money"], 1450)
|
||
|
|
|
||
|
|
def test_pay_to_leave_jail(self):
|
||
|
|
self.p.players[0]["inJail"] = True
|
||
|
|
self.p.parse_line("That cost you $50")
|
||
|
|
self.assertEqual(self.alice()["money"], 1450)
|
||
|
|
|
||
|
|
def test_jail_turn_indicator_1st(self):
|
||
|
|
self.p.parse_line("(This is your 1st turn in JAIL)")
|
||
|
|
# Informational
|
||
|
|
|
||
|
|
def test_jail_turn_indicator_2nd(self):
|
||
|
|
self.p.parse_line("(This is your 2nd turn in JAIL)")
|
||
|
|
|
||
|
|
def test_jail_turn_indicator_3rd(self):
|
||
|
|
self.p.parse_line("(This is your 3rd (and final) turn in JAIL)")
|
||
|
|
|
||
|
|
def test_not_in_jail_message(self):
|
||
|
|
self.p.parse_line("But you're not IN Jail")
|
||
|
|
# Should not crash
|
||
|
|
|
||
|
|
|
||
|
|
class TestCards(TestParserSetup):
|
||
|
|
"""Tests from cards.c: get_card() - Chance and Community Chest"""
|
||
|
|
|
||
|
|
def test_get_out_of_jail_free(self):
|
||
|
|
self.p.parse_line(">> GET OUT OF JAIL FREE <<")
|
||
|
|
self.p.parse_line("Keep this card until needed or sold")
|
||
|
|
# Parser should track GOJF cards
|
||
|
|
# This needs parser support
|
||
|
|
|
||
|
|
def test_card_money_gain(self):
|
||
|
|
# Community Chest: "Receive for Services $25"
|
||
|
|
self.p.parse_line("Receive for Services $25.")
|
||
|
|
# or "Bank error in your favor. Collect $200"
|
||
|
|
# or "You inherit $100"
|
||
|
|
|
||
|
|
def test_card_money_loss(self):
|
||
|
|
# "Doctor's fees. Pay $50"
|
||
|
|
# "Pay hospital $100"
|
||
|
|
pass
|
||
|
|
|
||
|
|
def test_card_advance_to_go(self):
|
||
|
|
self.p.parse_line("That puts you on === GO ===")
|
||
|
|
self.assertEqual(self.alice()["location"], 0)
|
||
|
|
|
||
|
|
def test_card_move_to_railroad(self):
|
||
|
|
# "Advance to the nearest Railroad"
|
||
|
|
self.p.parse_line("That puts you on Pennsylvania RR")
|
||
|
|
self.assertEqual(self.alice()["location"], 15)
|
||
|
|
|
||
|
|
def test_card_move_to_utility(self):
|
||
|
|
self.p.parse_line("That puts you on Water Works")
|
||
|
|
self.assertEqual(self.alice()["location"], 28)
|
||
|
|
|
||
|
|
def test_card_go_back_3(self):
|
||
|
|
self.p.players[0]["location"] = 10
|
||
|
|
# Card says go back 3 spaces
|
||
|
|
self.p.parse_line("That puts you on Chance i")
|
||
|
|
|
||
|
|
def test_card_repair_tax(self):
|
||
|
|
# "You had 3 Houses and 1 Hotels, so that cost you $190"
|
||
|
|
self.p.parse_line("You had 3 Houses and 1 Hotels, so that cost you $190")
|
||
|
|
self.assertEqual(self.alice()["money"], 1310)
|
||
|
|
|
||
|
|
def test_card_repair_zero(self):
|
||
|
|
self.p.parse_line("You had 0 Houses and 0 Hotels, so that cost you $0")
|
||
|
|
self.assertEqual(self.alice()["money"], 1500)
|
||
|
|
|
||
|
|
def test_card_collect_from_each_player(self):
|
||
|
|
# "Grand Opera Night. Collect $50 from every player"
|
||
|
|
# type_min == 'A': other players pay, current player receives
|
||
|
|
pass
|
||
|
|
|
||
|
|
def test_card_pay_each_player(self):
|
||
|
|
# "You are assessed for street repairs" type 'A'
|
||
|
|
pass
|
||
|
|
|
||
|
|
def test_card_delimiter(self):
|
||
|
|
# Cards are printed between "-----" lines
|
||
|
|
self.p.parse_line("------------------------------")
|
||
|
|
# Should not crash
|
||
|
|
|
||
|
|
|
||
|
|
class TestMortgage(TestParserSetup):
|
||
|
|
"""Tests from morg.c"""
|
||
|
|
|
||
|
|
def test_mortgage(self):
|
||
|
|
self.p.parse_line("Oriental ave. is mortgaged")
|
||
|
|
self.assertTrue(self.state()["squares"][6]["mortgaged"])
|
||
|
|
|
||
|
|
def test_unmortgage(self):
|
||
|
|
self.p.squares[6]["mortgaged"] = True
|
||
|
|
self.p.parse_line("Oriental ave. is unmortgaged")
|
||
|
|
self.assertFalse(self.state()["squares"][6]["mortgaged"])
|
||
|
|
|
||
|
|
|
||
|
|
class TestHouses(TestParserSetup):
|
||
|
|
"""Tests from houses.c: buy_h(), sell_h()"""
|
||
|
|
|
||
|
|
def test_houses_cost_message(self):
|
||
|
|
self.p.parse_line("Houses will cost $50")
|
||
|
|
# Informational
|
||
|
|
|
||
|
|
def test_buy_houses_confirmation(self):
|
||
|
|
self.p.parse_line("You asked for 3 houses for $150")
|
||
|
|
# Informational; actual state change comes from board/holdings
|
||
|
|
|
||
|
|
def test_sell_houses_confirmation(self):
|
||
|
|
self.p.parse_line("You asked to sell 2 houses for $50")
|
||
|
|
|
||
|
|
def test_house_listing(self):
|
||
|
|
# "Mediterranean (2) Baltic (1)"
|
||
|
|
pass
|
||
|
|
|
||
|
|
def test_hotel_listing(self):
|
||
|
|
# "Mediterranean (H)"
|
||
|
|
pass
|
||
|
|
|
||
|
|
|
||
|
|
class TestPlayerTurns(TestParserSetup):
|
||
|
|
"""Tests for turn tracking from status lines"""
|
||
|
|
|
||
|
|
def test_player_status_line(self):
|
||
|
|
self.p.parse_line("Bob (2) (cash $1400) on Vermont ave. (L)")
|
||
|
|
self.assertEqual(self.state()["currentPlayer"], 1)
|
||
|
|
self.assertEqual(self.bob()["money"], 1400)
|
||
|
|
|
||
|
|
def test_player_roll_for_order(self):
|
||
|
|
self.p.parse_line("Alice (1) rolls 10")
|
||
|
|
# Should detect Alice as player
|
||
|
|
|
||
|
|
def test_goes_first(self):
|
||
|
|
self.p.parse_line("Bob (2) goes first")
|
||
|
|
# Informational
|
||
|
|
|
||
|
|
def test_turn_switch(self):
|
||
|
|
self.p.parse_line("Alice (1) (cash $1500) on === GO ===")
|
||
|
|
self.assertEqual(self.state()["currentPlayer"], 0)
|
||
|
|
self.p.parse_line("Bob (2) (cash $1500) on === GO ===")
|
||
|
|
self.assertEqual(self.state()["currentPlayer"], 1)
|
||
|
|
self.p.parse_line("Charlie (3) (cash $1500) on === GO ===")
|
||
|
|
self.assertEqual(self.state()["currentPlayer"], 2)
|
||
|
|
|
||
|
|
|
||
|
|
class TestAutoDetectPlayers(unittest.TestCase):
|
||
|
|
"""Test that parser auto-detects players from game output."""
|
||
|
|
|
||
|
|
def test_auto_detect_from_status(self):
|
||
|
|
p = MonopParser()
|
||
|
|
p.parse_line("Alice (1) (cash $1500) on === GO ===")
|
||
|
|
p.parse_line("Bob (2) (cash $1500) on === GO ===")
|
||
|
|
state = p.get_state()
|
||
|
|
self.assertEqual(len(state["players"]), 2)
|
||
|
|
self.assertEqual(state["players"][0]["name"], "Alice")
|
||
|
|
self.assertEqual(state["players"][1]["name"], "Bob")
|
||
|
|
|
||
|
|
def test_auto_detect_from_rolls(self):
|
||
|
|
p = MonopParser()
|
||
|
|
p.parse_line("Alice (1) rolls 10")
|
||
|
|
p.parse_line("Bob (2) rolls 8")
|
||
|
|
p.parse_line("Charlie (3) rolls 6")
|
||
|
|
state = p.get_state()
|
||
|
|
self.assertEqual(len(state["players"]), 3)
|
||
|
|
|
||
|
|
|
||
|
|
class TestBankruptcy(TestParserSetup):
|
||
|
|
"""Tests from misc.c: force_morg(), is_not_pay()"""
|
||
|
|
|
||
|
|
def test_leaves_in_debt(self):
|
||
|
|
self.p.parse_line("That leaves you $500 in debt")
|
||
|
|
self.assertEqual(self.alice()["money"], -500)
|
||
|
|
|
||
|
|
def test_leaves_broke(self):
|
||
|
|
self.p.parse_line("that leaves you broke")
|
||
|
|
self.assertEqual(self.alice()["money"], 0)
|
||
|
|
|
||
|
|
|
||
|
|
class TestHoldings(TestParserSetup):
|
||
|
|
"""Tests from print.c: printhold()"""
|
||
|
|
|
||
|
|
def test_holdings_header(self):
|
||
|
|
self.p.parse_line("Alice's (1) holdings (Total worth: $1500):")
|
||
|
|
# Should enter holdings parsing mode
|
||
|
|
|
||
|
|
def test_holdings_cash(self):
|
||
|
|
self.p.parse_line("Alice's (1) holdings (Total worth: $1500):")
|
||
|
|
self.p.parse_line(" $1200")
|
||
|
|
self.assertEqual(self.alice()["money"], 1200)
|
||
|
|
|
||
|
|
def test_holdings_cash_with_gojf(self):
|
||
|
|
self.p.parse_line("Alice's (1) holdings (Total worth: $1700):")
|
||
|
|
self.p.parse_line(" $1200, 2 get-out-of-jail-free cards")
|
||
|
|
self.assertEqual(self.alice()["money"], 1200)
|
||
|
|
self.assertEqual(self.alice()["goJailFreeCards"], 2)
|
||
|
|
|
||
|
|
def test_board_header(self):
|
||
|
|
changed = self.p.parse_line("Name Own Price Mg # Rent")
|
||
|
|
# Should not crash
|
||
|
|
|
||
|
|
|
||
|
|
class TestEdgeCases(TestParserSetup):
|
||
|
|
"""Edge cases and unusual game states."""
|
||
|
|
|
||
|
|
def test_empty_line(self):
|
||
|
|
changed = self.p.parse_line("")
|
||
|
|
self.assertFalse(changed)
|
||
|
|
|
||
|
|
def test_command_prompt(self):
|
||
|
|
changed = self.p.parse_line("-- Command:")
|
||
|
|
self.assertFalse(changed)
|
||
|
|
|
||
|
|
def test_card_delimiter_line(self):
|
||
|
|
self.p.parse_line("------------------------------")
|
||
|
|
# Should not crash
|
||
|
|
|
||
|
|
def test_illegal_response(self):
|
||
|
|
self.p.parse_line('Illegal response: "yes". Use \'?\' to get list of valid answers')
|
||
|
|
# Should not crash
|
||
|
|
|
||
|
|
def test_multiple_community_chest(self):
|
||
|
|
# Game has 3 CC squares, names may have trailing spaces in game data
|
||
|
|
self.p.parse_line("That puts you on Community Chest ii")
|
||
|
|
# Should match one of the CC squares
|
||
|
|
|
||
|
|
def test_game_reset(self):
|
||
|
|
self.p.parse_line("How many players?")
|
||
|
|
state = self.state()
|
||
|
|
self.assertEqual(len(state["players"]), 0)
|
||
|
|
self.assertTrue(self.p.game_started)
|
||
|
|
|
||
|
|
def test_lucky_message(self):
|
||
|
|
# lucky() prints things like "You lucky dog!" or "What luck!"
|
||
|
|
self.p.parse_line("The thing is mortgaged. What luck!")
|
||
|
|
# Should not crash
|
||
|
|
|
||
|
|
def test_long_property_name(self):
|
||
|
|
self.p.parse_line("That puts you on N. Carolina ave. (G)")
|
||
|
|
self.assertEqual(self.alice()["location"], 32)
|
||
|
|
|
||
|
|
def test_short_line_rr(self):
|
||
|
|
self.p.parse_line("That puts you on Short Line RR")
|
||
|
|
self.assertEqual(self.alice()["location"], 35)
|
||
|
|
|
||
|
|
|
||
|
|
class TestMonopServerPrompts(unittest.TestCase):
|
||
|
|
"""Test that all prompts the game can ask are handled.
|
||
|
|
These are the prompts the monop_server needs to respond to."""
|
||
|
|
|
||
|
|
PROMPTS = [
|
||
|
|
# From spec.c
|
||
|
|
"Do you wish to lose 10%% of your total worth or $200? ",
|
||
|
|
# Valid: "10%", "ten percent", "%", "$200", "200"
|
||
|
|
|
||
|
|
# From prop.c
|
||
|
|
"Do you want to buy? ",
|
||
|
|
# Valid: "yes", "no"
|
||
|
|
|
||
|
|
# From getinp.c / misc.c
|
||
|
|
"Is that ok? ",
|
||
|
|
# Valid: "yes", "no"
|
||
|
|
|
||
|
|
# From jail.c (implicit - player chooses action)
|
||
|
|
# roll, card, pay
|
||
|
|
|
||
|
|
# From houses.c
|
||
|
|
"Which property do you wish to buy houses for? ",
|
||
|
|
"How many houses do you wish to buy for",
|
||
|
|
"Which property do you wish to sell houses from? ",
|
||
|
|
|
||
|
|
# From trade.c
|
||
|
|
"Which player do you wish to trade with? ",
|
||
|
|
# trade details prompts
|
||
|
|
|
||
|
|
# From morg.c
|
||
|
|
"Which piece of property do you wish to mortgage? ",
|
||
|
|
"Which piece of property do you wish to unmortgage? ",
|
||
|
|
|
||
|
|
# From execute.c
|
||
|
|
"Which file do you wish to save it in? ",
|
||
|
|
"Which file do you wish to restore from? ",
|
||
|
|
|
||
|
|
# From misc.c (force mortgage)
|
||
|
|
"Do you wish to sell any houses? ",
|
||
|
|
# Valid: "yes", "no"
|
||
|
|
|
||
|
|
# From prop.c (bidding)
|
||
|
|
"How much do you bid? ",
|
||
|
|
|
||
|
|
# Trade confirmation
|
||
|
|
# "<name>, is the trade ok? "
|
||
|
|
]
|
||
|
|
|
||
|
|
def test_all_prompts_documented(self):
|
||
|
|
"""Ensure we have a comprehensive list of prompts."""
|
||
|
|
self.assertTrue(len(self.PROMPTS) > 10)
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
unittest.main(verbosity=2)
|