-
Notifications
You must be signed in to change notification settings - Fork 0
/
SmallSailing
executable file
·134 lines (110 loc) · 4.24 KB
/
SmallSailing
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/usr/bin/env python3
# Copyright (c) 2019-2024 Benjamin Holt -- MIT License
"""
Tiny, seemingly pointless, interactive fiction game to play with `smallmachine`
"""
import random
import time
import smallmachine as sm
#####
### Helpers ###
def adlib(x, joiner=" "):
"Dynamically assemble messages from nested collections of parts. Tuples are pieces to be strung together, lists are variants to choose among; anything else is used as a string"
if type(x) is tuple:
return joiner.join( adlib(i) for i in x ) # Joining with "|" can be helpful to see how messages get put together
if type(x) is list:
return adlib(random.choice(x))
return str(x)
class Location:
def __init__(self, name, description):
self.name = name
self.description = description
# Demonstrate a simple enter-action pattern
def enter(self, **_):
"""Enter action: describe the location"""
return adlib(self.description)
def __str__(self):
return self.name
@classmethod
def enter_action(cls, state, destination, **ctx):
l = destination if destination is not ... else state
if isinstance(l, cls):
return l.enter(state=state, destination=destination, **ctx)
def input_in(*args):
return lambda input, **_: input in args
#####
### Fixtures ###
above = Location(
"above",
("You are on the deck of a small sailboat on a", ["calm", "serene", "blue", "clear", "glistening",], "sea; an open hatch leads down.")
)
below = Location(
"below",
("You are in the", ["cozy", "homey", "snug",], "cabin of a small boat, with just enough room for a bunk and a tiny desk with a logbook; a hatch leads up.")
)
messages = {
"sail": ("You", ["set", "adjust", "tack",], "your sail", ["to", "toward", "for"], "{}."),
"sleep": ("The bunk is", ["soft", "comfortable", "warm", "cozy",], "and you", ["rest", "sleep", "snooze", "nap", "doze",], ["well.", "deeply.", "blissfully.", "nicely"]),
"log": [("Weather was", ["fair.", "good.", "lovely.",]),
(
["Good", "Quick", "Slow",], "sailing",
[
"today",
("this", ["morning.", "afternoon.", "evening.",])
]
),
],
}
#####
### Actions ###
def sail_action(**_):
s = input("Where to? > ")
return adlib(messages["sail"]).format(s)
log_entries = [(["Fair", "Nice", "Brisk",], "weather."),] # Put one bogus entry in 'cos choose (in adlib) can't handle an empty list
def write_action(**_):
s = input("What do you want to say? > ")
log_entries.append(s)
return "Written"
def explode_action(**_):
raise ValueError("Ka-Boom!")
#####
### Rules ###
rules = {
"start": [
("start", lambda input, **_: input != "crash", Location.enter_action, above),
],
above: [
("go below", input_in("d", "down", "below"), Location.enter_action, below),
("sail", input_in("s", "sail"), sail_action, ...),
],
below: [
("go above", input_in("u", "up", "above"), Location.enter_action, above),
("read log", input_in("r", "read", "read logbook"), lambda **_: adlib([messages["log"], log_entries]), ...),
("write log", input_in("w", "write", "log"), write_action, ...),
("sleep", input_in("s", "sleep", "bunk", "lie down", "lay down", "nap"), lambda **_: adlib(messages["sleep"]), ...),
],
...: [
("look", input_in("l", "look"), Location.enter_action, ...),
("kaboom", "kaboom", explode_action, ...),
("warp", "warp", lambda **_: "Warp out of this dimension", "elsewhere"),
("anything except crash", lambda input, **_: input != "crash", lambda **_: "Sorry, you can't do that.", ...),
]
}
#####
### World ###
world = sm.StateMachine(rules, "start")
# world = sm.StateMachine(rules, "start", tracer=True)
# world = sm.StateMachine(rules, "start", tracer=sm.PrefixTracer("***"))
#####
### Main ###
print("Smooth Sailing", flush=True)
time.sleep(0.1) # HACK: wait for flush; sometimes prompt prints out-of-order with print output in spite of flush=True
out = world(input("Press enter to start. "))
if out:
print(out, flush=True)
while True:
time.sleep(0.1) # HACK: wait for flush
out = world(input("> "))
if out:
print(out, flush=True)
#####