ttr 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/ttr.rb +7 -0
- data/lib/ttr/entities/city.rb +38 -0
- data/lib/ttr/entities/game.rb +30 -0
- data/lib/ttr/entities/opponent.rb +43 -0
- data/lib/ttr/entities/player.rb +213 -0
- data/lib/ttr/entities/route.rb +74 -0
- data/lib/ttr/entities/ticket.rb +18 -0
- data/lib/ttr/objects/card.rb +14 -0
- data/lib/ttr/objects/city.rb +51 -0
- data/lib/ttr/objects/colorful.rb +30 -0
- data/lib/ttr/objects/deck.rb +81 -0
- data/lib/ttr/objects/entity.rb +15 -0
- data/lib/ttr/objects/game.rb +44 -0
- data/lib/ttr/objects/map.rb +49 -0
- data/lib/ttr/objects/player.rb +170 -0
- data/lib/ttr/objects/route.rb +40 -0
- data/lib/ttr/objects/ticket.rb +24 -0
- data/lib/ttr/objects/usa_map.rb +177 -0
- data/lib/ttr/sample/script.rb +33 -0
- metadata +72 -0
data/lib/ttr.rb
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright © 2011 Jesse Sielaff
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
class City < Entity
|
|
7
|
+
# Returns true if the two Cities are connected by a single direct Route, false
|
|
8
|
+
# otherwise.
|
|
9
|
+
#
|
|
10
|
+
def connected? (city)
|
|
11
|
+
@obj.connected?(City.object(city))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Returns an Array of Cities connected to the City by a single direct Route.
|
|
15
|
+
#
|
|
16
|
+
def connected_cities
|
|
17
|
+
City.entities(@obj.connected_city_objs)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Returns the name of the City as a Symbol.
|
|
21
|
+
#
|
|
22
|
+
def name
|
|
23
|
+
@obj.name
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Returns an Array of Routes leading into and out of the City.
|
|
27
|
+
#
|
|
28
|
+
def routes
|
|
29
|
+
Route.entities(@obj.route_objs)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Returns an Array of Routes leading into and out of the City that are not
|
|
33
|
+
# claimed by any Player.
|
|
34
|
+
#
|
|
35
|
+
def unclaimed_routes
|
|
36
|
+
Route.entities(@obj.route_objs.reject {|obj| obj.player_obj })
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright © 2011 Jesse Sielaff
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
class Game < Entity
|
|
7
|
+
# Returns an Array of the colors of the Cards available for selection.
|
|
8
|
+
#
|
|
9
|
+
def available_colors
|
|
10
|
+
@obj.deck.available_colors
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Returns an Array of all Cities in the Game.
|
|
14
|
+
#
|
|
15
|
+
def cities
|
|
16
|
+
City.entities(@obj.city_objs)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Returns an Array of all Routes in the Game.
|
|
20
|
+
#
|
|
21
|
+
def routes
|
|
22
|
+
Route.entities(@obj.route_objs)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Returns an Array of all unclaimed Routes in the Game.
|
|
26
|
+
#
|
|
27
|
+
def unclaimed_routes
|
|
28
|
+
Route.entities(@obj.route_objs.reject {|obj| obj.player_obj })
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright © 2011 Jesse Sielaff
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
class Opponent < Entity
|
|
7
|
+
# Returns the number of Cards held by the Opponent.
|
|
8
|
+
#
|
|
9
|
+
def cards
|
|
10
|
+
@obj.cards.values.flatten.length
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Returns an Array of the Routes claimed by the Opponent.
|
|
14
|
+
#
|
|
15
|
+
def claimed_routes
|
|
16
|
+
Route.entities(@obj.claimed_route_objs)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Returns the current score of the Opponent.
|
|
20
|
+
#
|
|
21
|
+
def score
|
|
22
|
+
@obj.score
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Returns an Array of the color of the last n Cards selected by the Opponent.
|
|
26
|
+
# Cards drawn at random are represented as :grey.
|
|
27
|
+
#
|
|
28
|
+
def selections (n)
|
|
29
|
+
@obj.selections.first(n)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Returns the number of Tickets held by the Player.
|
|
33
|
+
#
|
|
34
|
+
def tickets
|
|
35
|
+
@obj.kept_ticket_objs.length
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Returns the number of unplayed trains held by the Player.
|
|
39
|
+
#
|
|
40
|
+
def trains
|
|
41
|
+
@obj.trains
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright © 2011 Jesse Sielaff
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
class Player < Entity
|
|
7
|
+
# Returns an Array of the Tickets currently available for the Player to choose
|
|
8
|
+
# to keep or discard.
|
|
9
|
+
#
|
|
10
|
+
def candidate_tickets
|
|
11
|
+
Ticket.entities(@obj.candidate_ticket_objs)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Returns the number of Cards of the given color held by the Player.
|
|
15
|
+
#
|
|
16
|
+
def cards (color)
|
|
17
|
+
@obj.cards[color].length
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Claims the given Route using Cards of the given color and the minimum number
|
|
21
|
+
# of wild Cards needed, then returns an Array of the Routes claimed by the
|
|
22
|
+
# Player. If the Player is unable to claim the Route with the given color,
|
|
23
|
+
# returns nil.
|
|
24
|
+
#
|
|
25
|
+
def claim (route, color)
|
|
26
|
+
return if @obj.action_begun
|
|
27
|
+
return if route.claimed?
|
|
28
|
+
return unless route.match?(color)
|
|
29
|
+
return if route.length > @obj.trains
|
|
30
|
+
return if (twin = Route.object(route).twin) && (twin.player_obj == Player.object(self))
|
|
31
|
+
return if (twin) && (opponents.length < 2) && (twin.player_obj)
|
|
32
|
+
return unless card_objs = @obj.set_of(route.length, color, wilds)
|
|
33
|
+
|
|
34
|
+
@obj.discard card_objs
|
|
35
|
+
@obj.claim Route.object(route)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Claims the given Route using all the Player's wild Cards and as many Cards
|
|
39
|
+
# of the given color as needed, then returns an Array of the Routes claimed by
|
|
40
|
+
# the Player. If the Player is unable to claim the Route with the given color,
|
|
41
|
+
# returns nil.
|
|
42
|
+
#
|
|
43
|
+
def claim_max_wild (route, color)
|
|
44
|
+
return if @obj.action_begun
|
|
45
|
+
return if route.claimed?
|
|
46
|
+
return unless route.match?(color)
|
|
47
|
+
return if route.length > @obj.trains
|
|
48
|
+
return if (twin = Route.object(route).twin) && (twin.player_obj == Player.object(self))
|
|
49
|
+
return if (twin) && (opponents.length < 2) && (twin.player_obj)
|
|
50
|
+
return unless card_objs = @obj.set_of(route.length, color, wilds, true)
|
|
51
|
+
|
|
52
|
+
@obj.discard card_objs
|
|
53
|
+
@obj.claim Route.object(route)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Claims the given Route using the given number of wild Cards and as many
|
|
57
|
+
# Cards of the given color as needed, then returns an Array of the Routes
|
|
58
|
+
# claimed by the Player. If the Player is unable to claim the Route with the
|
|
59
|
+
# given color, returns nil.
|
|
60
|
+
#
|
|
61
|
+
def claim_n_wild (route, color, n)
|
|
62
|
+
return if @obj.action_begun
|
|
63
|
+
return if route.claimed?
|
|
64
|
+
return unless route.match?(color)
|
|
65
|
+
return if route.length > @obj.trains
|
|
66
|
+
return if (twin = Route.object(route).twin) && (twin.player_obj == Player.object(self))
|
|
67
|
+
return if (twin) && (opponents.length < 2) && (twin.player_obj)
|
|
68
|
+
return unless card_objs = @obj.set_of(route.length, color, n, true)
|
|
69
|
+
|
|
70
|
+
@obj.discard card_objs
|
|
71
|
+
@obj.claim Route.object(route)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Claims the given Route using only Cards of the given color (no wild Cards),
|
|
75
|
+
# then returns an Array of the Routes claimed by the Player. If the Player is
|
|
76
|
+
# unable to claim the Route with the given color, returns nil.
|
|
77
|
+
#
|
|
78
|
+
def claim_no_wild (route, color)
|
|
79
|
+
return if @obj.action_begun
|
|
80
|
+
return if route.claimed?
|
|
81
|
+
return unless route.match?(color)
|
|
82
|
+
return if route.length > @obj.trains
|
|
83
|
+
return if (twin = Route.object(route).twin) && (twin.player_obj == Player.object(self))
|
|
84
|
+
return if (twin) && (opponents.length < 2) && (twin.player_obj)
|
|
85
|
+
return unless card_objs = @obj.set_of(route.length, color, 0)
|
|
86
|
+
|
|
87
|
+
@obj.discard card_objs
|
|
88
|
+
@obj.claim Route.object(route)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Returns an Array of the Routes claimed by the Player.
|
|
92
|
+
#
|
|
93
|
+
def claimed_routes
|
|
94
|
+
Route.entities(@obj.claimed_route_objs)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Removes all candidate Tickets from the Player, marks the Player's turn as
|
|
98
|
+
# complete, and returns true. If the Player is unable to discard Tickets,
|
|
99
|
+
# returns nil.
|
|
100
|
+
#
|
|
101
|
+
def discard_tickets
|
|
102
|
+
return if @obj.kept_ticket_objs.length < 2
|
|
103
|
+
return if [0,3].include?(@obj.candidate_ticket_objs.length)
|
|
104
|
+
|
|
105
|
+
@obj.clear_ticket_objs
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Adds one Card to the Player and returns the color of that Card. If the
|
|
109
|
+
# Player is unable to draw a Card, returns nil.
|
|
110
|
+
#
|
|
111
|
+
def draw_random
|
|
112
|
+
return if @obj.draws_remaining < 1
|
|
113
|
+
return unless card = @obj.deck.draw
|
|
114
|
+
|
|
115
|
+
@obj.cards[card.color] << card
|
|
116
|
+
@obj.selections.unshift(:grey)
|
|
117
|
+
@obj.register_draw(1)
|
|
118
|
+
|
|
119
|
+
card.color
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Adds three candidate Tickets to the Player and returns an Array of the
|
|
123
|
+
# Tickets. Disallows the Player from claiming Routes, drawing Tickets, and
|
|
124
|
+
# drawing Cards for the remainder of the turn. Returns nil if the Player is
|
|
125
|
+
# unable to draw Tickets.
|
|
126
|
+
#
|
|
127
|
+
def draw_tickets
|
|
128
|
+
return if @obj.action_begun
|
|
129
|
+
return if @obj.remaining_ticket_objs.length < 3
|
|
130
|
+
|
|
131
|
+
Ticket.entities(@obj.add_tickets)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Marks the given Ticket as kept by the Player. If all candidate Tickets have
|
|
135
|
+
# been kept, marks the Player's turn as complete and returns true. Returns
|
|
136
|
+
# false if some candidate Tickets remain, or nil if the Ticket was not a
|
|
137
|
+
# candidate Ticket.
|
|
138
|
+
#
|
|
139
|
+
def keep_ticket (ticket)
|
|
140
|
+
ticket_obj = Ticket.object(ticket)
|
|
141
|
+
return unless @obj.candidate_ticket_objs.delete(ticket_obj)
|
|
142
|
+
|
|
143
|
+
@obj.keep_ticket_obj(ticket_obj) || false
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Returns an Array of the Tickets kept by the Player.
|
|
147
|
+
#
|
|
148
|
+
def kept_tickets
|
|
149
|
+
Ticket.entities(@obj.kept_ticket_objs)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Returns an Array of the Player's Opponents.
|
|
153
|
+
#
|
|
154
|
+
def opponents
|
|
155
|
+
Opponent.entities(@obj.all_player_objs - [@obj])
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Returns the current score of the Player.
|
|
159
|
+
#
|
|
160
|
+
def score
|
|
161
|
+
@obj.score
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Moves one Card of the given Color from the selection to the Player and
|
|
165
|
+
# returns the number of Cards of that color held by the Player. If the Player
|
|
166
|
+
# is not currently allowed to draw a Card of the given color, returns nil.
|
|
167
|
+
#
|
|
168
|
+
def select_card (color)
|
|
169
|
+
return select_wild if color == :wild
|
|
170
|
+
|
|
171
|
+
return if @obj.draws_remaining < 1
|
|
172
|
+
return unless card = @obj.deck.select(color)
|
|
173
|
+
|
|
174
|
+
@obj.cards[color] << card
|
|
175
|
+
@obj.selections.unshift(card.color)
|
|
176
|
+
@obj.register_draw(1)
|
|
177
|
+
|
|
178
|
+
cards(color)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Moves one wild Card from the selection to the Player and returns the number
|
|
182
|
+
# of wild Cards held by the Player. If the Player is not currently allowed to
|
|
183
|
+
# draw a wild Card, returns nil.
|
|
184
|
+
#
|
|
185
|
+
def select_wild
|
|
186
|
+
return if @obj.draws_remaining < 2
|
|
187
|
+
return unless card = @obj.deck.select(:wild)
|
|
188
|
+
|
|
189
|
+
@obj.cards[:wild] << card
|
|
190
|
+
@obj.selections.unshift(:wild)
|
|
191
|
+
@obj.register_draw(2)
|
|
192
|
+
|
|
193
|
+
wilds
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Returns the number of unplayed trains held by the Player.
|
|
197
|
+
#
|
|
198
|
+
def trains
|
|
199
|
+
@obj.trains
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Returns true if the Player has completed the current turn.
|
|
203
|
+
#
|
|
204
|
+
def turn_completed?
|
|
205
|
+
@obj.turn_completed
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Returns the number of wild Cards held by the Player.
|
|
209
|
+
#
|
|
210
|
+
def wilds
|
|
211
|
+
cards(:wild)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright © 2011 Jesse Sielaff
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
class Route < Entity
|
|
7
|
+
# Returns an Array of the two Cities at either end of the Route.
|
|
8
|
+
#
|
|
9
|
+
def cities
|
|
10
|
+
City.entities(@obj.city_objs)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Returns true if any Player has claimed the Route, false otherwise.
|
|
14
|
+
#
|
|
15
|
+
def claimed?
|
|
16
|
+
@obj.player_obj != nil
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Returns true if the given Player has claimed the route, false otherwise.
|
|
20
|
+
#
|
|
21
|
+
def claimed_by_player? (player)
|
|
22
|
+
@obj.player_obj == Player.object(player)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Returns the color of the Route as a Symbol.
|
|
26
|
+
#
|
|
27
|
+
def color
|
|
28
|
+
@obj.color
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Returns false if the color of the Route is :grey, true otherwise.
|
|
32
|
+
#
|
|
33
|
+
def color?
|
|
34
|
+
@obj.color != :grey
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Returns true if the Route is the exact same color as the given color, false
|
|
38
|
+
# otherwise.
|
|
39
|
+
#
|
|
40
|
+
def exact_match? (color)
|
|
41
|
+
@obj.color == color
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Returns true if the color of the Route is :grey, false otherwise.
|
|
45
|
+
#
|
|
46
|
+
def grey?
|
|
47
|
+
@obj.color == :grey
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Returns the length of the Route.
|
|
51
|
+
#
|
|
52
|
+
def length
|
|
53
|
+
@obj.length
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Returns true if the given color represents a color match with the Route,
|
|
57
|
+
# false otherwise. The color :grey matches every color.
|
|
58
|
+
#
|
|
59
|
+
def match? (color)
|
|
60
|
+
[color, :grey].include?(@obj.color)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Returns the point value of the Route.
|
|
64
|
+
#
|
|
65
|
+
def points
|
|
66
|
+
@obj.points
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Returns true if the Route is unclaimed, false otherwise.
|
|
70
|
+
#
|
|
71
|
+
def unclaimed?
|
|
72
|
+
@obj.player_obj == nil
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright © 2011 Jesse Sielaff
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
class Ticket < Entity
|
|
7
|
+
# Returns an Array of the Cities on the Ticket.
|
|
8
|
+
#
|
|
9
|
+
def cities
|
|
10
|
+
City.entities(@obj.city_objs)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Returns the point value of the Ticket.
|
|
14
|
+
#
|
|
15
|
+
def points
|
|
16
|
+
@obj.points
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright © 2011 Jesse Sielaff
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
class CityObject
|
|
7
|
+
def initialize (name)
|
|
8
|
+
@name = name
|
|
9
|
+
@route_objs = []
|
|
10
|
+
@connected_city_objs = []
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
attr_reader :connected_city_objs, :name, :route_objs
|
|
14
|
+
|
|
15
|
+
# Adds the given Route to the City's Routes.
|
|
16
|
+
#
|
|
17
|
+
def add_route_obj (route_obj)
|
|
18
|
+
@route_objs << route_obj
|
|
19
|
+
@connected_city_objs |= (route_obj.city_objs - [self])
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Returns true if the two Cities are connected by a single direct Route, false
|
|
23
|
+
# otherwise.
|
|
24
|
+
#
|
|
25
|
+
def connected? (city_obj)
|
|
26
|
+
@connected_city_objs.include?(city_obj)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Returns true if the two Cities are connected using any combination of the
|
|
30
|
+
# given Routes.
|
|
31
|
+
#
|
|
32
|
+
def connected_using? (city_obj, route_objs)
|
|
33
|
+
valid_routes = route_objs & @route_objs
|
|
34
|
+
return false if valid_routes.empty?
|
|
35
|
+
|
|
36
|
+
valid_routes.any? do |obj|
|
|
37
|
+
return true if obj.connects?(self, city_obj)
|
|
38
|
+
|
|
39
|
+
obj.city_objs.any? do |c_obj|
|
|
40
|
+
next if c_obj == self
|
|
41
|
+
c_obj.connected_using?(city_obj, route_objs - valid_routes)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Returns an Array of the Routes directly connecting the two Cities.
|
|
47
|
+
#
|
|
48
|
+
def routes_to (city_obj)
|
|
49
|
+
@route_objs.select {|obj| obj.connects?(self, city_obj) }
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright © 2011 Jesse Sielaff
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
module Colorful
|
|
7
|
+
attr_reader :color
|
|
8
|
+
|
|
9
|
+
# Returns true if the given Symbol or Colorful object represents the exact
|
|
10
|
+
# same color as the receiver, false otherwise. Returns nil if the given object
|
|
11
|
+
# is neither a Symbol nor Colorful.
|
|
12
|
+
#
|
|
13
|
+
def exact_match? (c)
|
|
14
|
+
case c
|
|
15
|
+
when Symbol then @color == c
|
|
16
|
+
when Colorful then exact_match? c.color
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Returns true if the given Symbol or Colorful object represents a color match
|
|
21
|
+
# with the receiver, false otherwise. The colors :wild and :grey match every
|
|
22
|
+
# color. Returns nil if the given object is neither a Symbol nor Colorful.
|
|
23
|
+
#
|
|
24
|
+
def match? (c)
|
|
25
|
+
case c
|
|
26
|
+
when Symbol then [:grey, :wild, @color].include? c
|
|
27
|
+
when Colorful then match? c.color
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright © 2011 Jesse Sielaff
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
class DeckObject
|
|
7
|
+
def initialize
|
|
8
|
+
@draw_pile = []
|
|
9
|
+
@selection = []
|
|
10
|
+
@discard_pile = 14.times.map { CardObject.new(:wild) }
|
|
11
|
+
|
|
12
|
+
%w:black blue green orange pink red white yellow:.each do |color|
|
|
13
|
+
@discard_pile.concat(12.times.map { CardObject.new(color.to_sym) })
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Returns an Array of the colors of the Cards in the selection.
|
|
18
|
+
#
|
|
19
|
+
def available_colors
|
|
20
|
+
@selection.map {|card| card.color }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Moves the given Card to the discard pile.
|
|
24
|
+
#
|
|
25
|
+
def discard (card)
|
|
26
|
+
@discard_pile << card
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Returns the top Card of the Deck, shuffling first if necessary. If there are
|
|
30
|
+
# no Cards left in the Deck, returns nil.
|
|
31
|
+
#
|
|
32
|
+
def draw
|
|
33
|
+
if @draw_pile.empty?
|
|
34
|
+
@draw_pile.replace(@discard_pile.shuffle)
|
|
35
|
+
@discard_pile.clear
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
@draw_pile.pop
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Returns true if there are no Cards remaining in the Deck, false otherwise.
|
|
42
|
+
#
|
|
43
|
+
def empty?
|
|
44
|
+
(@draw_pile + @discard_pile + @selection).empty?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Moves Cards from the draw pile to the selection (shuffling when necessary)
|
|
48
|
+
# until the selection contains 5 Cards, or as many Cards as are left in the
|
|
49
|
+
# Deck. If there are ever 3 wild Cards showing (and at least 3 non-wild cards
|
|
50
|
+
# left in the Deck), moves the selection Cards to the discard pile and
|
|
51
|
+
# attempts to fill the selection again.
|
|
52
|
+
#
|
|
53
|
+
def fill_selection
|
|
54
|
+
until @selection.length > 4
|
|
55
|
+
return unless card = draw
|
|
56
|
+
@selection << card
|
|
57
|
+
|
|
58
|
+
if (@selection.count {|card| card.color == :wild } >= 3) && ((@draw_pile + @discard_pile + @selection).count {|card| card.color != :wild } > 2)
|
|
59
|
+
@selection.each {|card| discard(card) }
|
|
60
|
+
@selection.clear
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Removes a Card of the given color from the selection, refills the selection,
|
|
66
|
+
# then returns the Card. If no Card of the given color is found, returns nil.
|
|
67
|
+
#
|
|
68
|
+
def select (color)
|
|
69
|
+
if index = @selection.index {|card| card.exact_match?(color) }
|
|
70
|
+
card = @selection.delete_at(index)
|
|
71
|
+
fill_selection
|
|
72
|
+
card
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Returns true if the deck is empty except for wild Cards in the selection.
|
|
77
|
+
#
|
|
78
|
+
def wilds_only?
|
|
79
|
+
(@draw_pile + @discard_pile).empty? && @selection.all? {|card| card.color == :wild }
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright © 2011 Jesse Sielaff
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
class Entity
|
|
7
|
+
def Entity.inherited (klass)
|
|
8
|
+
klass.define_singleton_method(:object) {|obj| obj.instance_variable_get(:@obj) }
|
|
9
|
+
klass.define_singleton_method(:entities) {|ary| ary.map {|obj| klass.new(obj) } }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize (obj)
|
|
13
|
+
@obj = obj
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright © 2011 Jesse Sielaff
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
class GameObject
|
|
7
|
+
def initialize (script_names, city_objs, route_objs, ticket_objs)
|
|
8
|
+
n = script_names.length
|
|
9
|
+
raise(NumberOfPlayers, "#{n} player#{"s" if n != 1} given, 2..5 players expected") unless n.between?(2,5)
|
|
10
|
+
|
|
11
|
+
@deck = DeckObject.new
|
|
12
|
+
|
|
13
|
+
@city_objs = city_objs
|
|
14
|
+
@route_objs = route_objs
|
|
15
|
+
@ticket_objs = ticket_objs
|
|
16
|
+
|
|
17
|
+
@player_objs = []
|
|
18
|
+
|
|
19
|
+
script_names.each do |script_name|
|
|
20
|
+
script = Script.method(script_name).to_proc.curry[Game.new(self)]
|
|
21
|
+
@player_objs << PlayerObject.new(@deck, @player_objs, @route_objs, script, @ticket_objs)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
@deck.fill_selection
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
NumberOfPlayers = Class.new(Exception)
|
|
28
|
+
UnfinishedTurn = Class.new(Exception)
|
|
29
|
+
|
|
30
|
+
attr_reader :deck, :route_objs
|
|
31
|
+
|
|
32
|
+
# Causes Players to take turns until every Player has played a turn in which
|
|
33
|
+
# at least one Player has fewer than 3 trains, then scores the Tickets held by
|
|
34
|
+
# each Player.
|
|
35
|
+
#
|
|
36
|
+
def play
|
|
37
|
+
@player_objs.cycle do |obj|
|
|
38
|
+
obj.take_a_turn
|
|
39
|
+
break if @player_objs.all? {|obj| obj.played_final_round? }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
@player_objs.map {|obj| obj.score_tickets; obj.score }
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright © 2011 Jesse Sielaff
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
class MapObject
|
|
7
|
+
def MapObject.inherited (subclass)
|
|
8
|
+
subclass.define_singleton_method(:play_game) do |*script_names|
|
|
9
|
+
subclass.new.new_game(*script_names).play
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize
|
|
14
|
+
@city_objs = []
|
|
15
|
+
@route_objs = []
|
|
16
|
+
@ticket_objs = []
|
|
17
|
+
|
|
18
|
+
build
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Creates and returns a new City on the Map.
|
|
22
|
+
#
|
|
23
|
+
def city (name)
|
|
24
|
+
@city_objs[0...0] = CityObject.new(name.to_sym)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Returns a new Game played using the Map, using Players running the named
|
|
28
|
+
# Scripts.
|
|
29
|
+
#
|
|
30
|
+
def new_game (*script_names)
|
|
31
|
+
GameObject.new(script_names, @city_objs, @route_objs.each {|r| r.player_obj = nil }, @ticket_objs.shuffle)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Creates and returns a new Route between the given Cities, with the given
|
|
35
|
+
# length and color.
|
|
36
|
+
#
|
|
37
|
+
def route (*city_objs, length, color)
|
|
38
|
+
route_obj = RouteObject.new(city_objs, length, color)
|
|
39
|
+
city_objs.each {|obj| obj.add_route_obj(route_obj) }
|
|
40
|
+
@route_objs[0...0] = route_obj
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Creates and returns a new Ticket for the given Cities, with the given point
|
|
44
|
+
# value.
|
|
45
|
+
#
|
|
46
|
+
def ticket (points, *city_objs)
|
|
47
|
+
@ticket_objs[0...0] = TicketObject.new(points, city_objs)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright © 2011 Jesse Sielaff
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
class PlayerObject
|
|
7
|
+
def initialize (deck_obj, player_objs, route_objs, script, ticket_objs)
|
|
8
|
+
@deck = deck_obj
|
|
9
|
+
@script = script
|
|
10
|
+
|
|
11
|
+
@all_player_objs = player_objs
|
|
12
|
+
@all_route_objs = route_objs
|
|
13
|
+
@remaining_ticket_objs = ticket_objs
|
|
14
|
+
|
|
15
|
+
@candidate_ticket_objs = []
|
|
16
|
+
@kept_ticket_objs = []
|
|
17
|
+
@claimed_route_objs = []
|
|
18
|
+
|
|
19
|
+
@score = 0
|
|
20
|
+
@trains = 45
|
|
21
|
+
|
|
22
|
+
@selections = []
|
|
23
|
+
@cards = { wild:[], black:[], blue:[], green:[], orange:[], pink:[], red:[], white:[], yellow:[] }
|
|
24
|
+
|
|
25
|
+
4.times do
|
|
26
|
+
card = @deck.draw
|
|
27
|
+
@cards[card.color] << card
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
@action_begun = false
|
|
31
|
+
@turn_completed = false
|
|
32
|
+
@draws_remaining = 0
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
attr_reader :action_begun, :all_player_objs, :candidate_ticket_objs, :cards,
|
|
36
|
+
:claimed_route_objs, :deck, :draws_remaining, :kept_ticket_objs,
|
|
37
|
+
:remaining_ticket_objs, :score, :selections, :trains,
|
|
38
|
+
:turn_completed
|
|
39
|
+
|
|
40
|
+
# Moves three candidate Tickets from the remaining Tickets to the Player and
|
|
41
|
+
# returns an Array of the Tickets. Disallows the Player from claiming Routes,
|
|
42
|
+
# drawing Tickets, and drawing Cards for the remainder of the turn.
|
|
43
|
+
#
|
|
44
|
+
def add_tickets
|
|
45
|
+
@action_begun = true
|
|
46
|
+
@draws_remaining = 0
|
|
47
|
+
|
|
48
|
+
@candidate_ticket_objs.replace(@remaining_ticket_objs.pop(3))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Marks the given Route as claimed by the Player. Increments the Player's
|
|
52
|
+
# score by the point value of the Route. Decrements the Player's trains by the
|
|
53
|
+
# length of the Route. Disallows claiming Routes, drawing Tickets, and drawing
|
|
54
|
+
# Cards for the remainder of the turn and marks the Player's turn as complete.
|
|
55
|
+
#
|
|
56
|
+
def claim (route_obj)
|
|
57
|
+
route_obj.player_obj = self
|
|
58
|
+
|
|
59
|
+
@claimed_route_objs << route_obj
|
|
60
|
+
@trains -= route_obj.length
|
|
61
|
+
@score += route_obj.points
|
|
62
|
+
|
|
63
|
+
@action_begun = true
|
|
64
|
+
@turn_completed = true
|
|
65
|
+
@draws_remaining = 0
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Removes all candidate Tickets from the Player, marks the Player's turn as
|
|
69
|
+
# complete, and returns true.
|
|
70
|
+
#
|
|
71
|
+
def clear_ticket_objs
|
|
72
|
+
@candidate_ticket_objs.clear
|
|
73
|
+
@turn_completed = true
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Moves the given Cards from the Player to the Deck's discard pile.
|
|
77
|
+
#
|
|
78
|
+
def discard (card_objs)
|
|
79
|
+
card_objs.each do |obj|
|
|
80
|
+
@cards.each {|k,v| v.delete(obj) }
|
|
81
|
+
@deck.discard(obj)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Marks the given Ticket as kept by the Player. If all candidate Tickets have
|
|
86
|
+
# been kept, marks the Player's turn as complete and returns true, otherwise
|
|
87
|
+
# returns nil.
|
|
88
|
+
#
|
|
89
|
+
def keep_ticket_obj (ticket_obj)
|
|
90
|
+
@kept_ticket_objs << ticket_obj
|
|
91
|
+
@turn_completed = true if @candidate_ticket_objs.empty?
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Returns true if the Player has taken a turn during the final round, false
|
|
95
|
+
# otherwise.
|
|
96
|
+
#
|
|
97
|
+
def played_final_round?
|
|
98
|
+
@final_round
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Returns true if the Player will be able to draw Cards, claim a Route, or
|
|
102
|
+
# draw Tickets during this turn, false otherwise.
|
|
103
|
+
#
|
|
104
|
+
def possible_to_play?
|
|
105
|
+
return true unless @deck.empty?
|
|
106
|
+
return true unless @remaining_ticket_objs.length < 3
|
|
107
|
+
|
|
108
|
+
max_route = @cards.keys.each_with_object({}) do |color, h|
|
|
109
|
+
next if color == :wild
|
|
110
|
+
h[color] = @cards[color].length + @cards[:wild].length
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
max_route[:grey] = max_route.values.max
|
|
114
|
+
|
|
115
|
+
@all_route_objs.any? do |obj|
|
|
116
|
+
next if obj.player_obj
|
|
117
|
+
next if obj.length > @trains
|
|
118
|
+
next if obj.length > max_route[obj.color]
|
|
119
|
+
next if (twin = obj.twin) && (twin.player_obj == self)
|
|
120
|
+
next if (twin) && (@all_player_objs.length < 3) && (twin.player_obj)
|
|
121
|
+
true
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Decrements the number of Cards left to draw by 1 and disallows the Player
|
|
126
|
+
# from drawing Tickets and claiming Routes for the remainder of this turn. If
|
|
127
|
+
# the number of Cards left to draw reaches 0, also disallows the Player from
|
|
128
|
+
# drawing more Cards this turn, and marks the Player's turn as complete.
|
|
129
|
+
#
|
|
130
|
+
def register_draw (n)
|
|
131
|
+
@action_begun = true
|
|
132
|
+
@draws_remaining -= n
|
|
133
|
+
|
|
134
|
+
if (@draws_remaining < 1) || @deck.empty? || @deck.wilds_only?
|
|
135
|
+
@turn_completed = true
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def score_tickets
|
|
140
|
+
@kept_ticket_objs.each {|obj| @score += (obj.completed? @claimed_route_objs) ? obj.points : -obj.points }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Returns an Array of Cards of the given size. If force_wilds is false, will
|
|
144
|
+
# attempt to fill the Array first with Cards of the given color, then with the
|
|
145
|
+
# given number of wild Cards (if necessary). If force_wilds is true, will
|
|
146
|
+
# attempt to fill the Array first with the given number of wild Cards, then
|
|
147
|
+
# with Cards of the given color (if necessary). If the Array is not filled to
|
|
148
|
+
# the required size, returns nil.
|
|
149
|
+
#
|
|
150
|
+
def set_of (size, color, wilds, force_wilds = false)
|
|
151
|
+
c, w = @cards[color], @cards[:wild].take(wilds)
|
|
152
|
+
|
|
153
|
+
set = (force_wilds) ? (w + c) : (c + w)
|
|
154
|
+
set.take(size) unless set.length < size
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
#
|
|
158
|
+
#
|
|
159
|
+
def take_a_turn
|
|
160
|
+
if possible_to_play?
|
|
161
|
+
@script[Player.new(self)]
|
|
162
|
+
raise Game::UnfinishedTurn unless @turn_completed
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
@final_round = @all_player_objs.any? {|obj| obj.trains < 3 }
|
|
166
|
+
@draws_remaining = 2
|
|
167
|
+
@action_begun = false
|
|
168
|
+
@turn_completed = false
|
|
169
|
+
end
|
|
170
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright © 2011 Jesse Sielaff
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
class RouteObject
|
|
7
|
+
def initialize (city_objs, length, color)
|
|
8
|
+
@city_objs = city_objs
|
|
9
|
+
@color = color
|
|
10
|
+
@length = length
|
|
11
|
+
@player_obj = nil
|
|
12
|
+
|
|
13
|
+
x = length.to_f
|
|
14
|
+
@points = (Math.sin(4*x/3)*x*x*x/250 + 23*x*x/78 + 7*x/13).round
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
include Colorful
|
|
18
|
+
|
|
19
|
+
attr_accessor :player_obj
|
|
20
|
+
attr_reader :city_objs, :length, :points
|
|
21
|
+
|
|
22
|
+
# Returns true if the Route connects the given Cities, false otherwise.
|
|
23
|
+
#
|
|
24
|
+
def connects? (*city_objs)
|
|
25
|
+
(@city_objs & city_objs).length == 2
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Returns true if the Route connects to the given City, false otherwise.
|
|
29
|
+
#
|
|
30
|
+
def into? (city_obj)
|
|
31
|
+
@city_objs.include?(city_obj)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# If there are two Routes connecting the two Cities at the end of this Route,
|
|
35
|
+
# returns the other Route. Otherwise, returns nil.
|
|
36
|
+
#
|
|
37
|
+
def twin
|
|
38
|
+
(@city_objs[0].routes_to(@city_objs[1]) - [self])[0]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright © 2011 Jesse Sielaff
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
class TicketObject
|
|
7
|
+
def initialize (points, city_objs)
|
|
8
|
+
@city_objs = city_objs
|
|
9
|
+
@points = points
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
attr_reader :city_objs, :points
|
|
13
|
+
|
|
14
|
+
# Returns true if the Ticket is completed by any combination of the given
|
|
15
|
+
# Routes, false otherwise.
|
|
16
|
+
#
|
|
17
|
+
def completed? (route_objs)
|
|
18
|
+
c1, c2 = city_objs
|
|
19
|
+
return false unless route_objs.any? {|obj| obj.into?(c1) }
|
|
20
|
+
return false unless route_objs.any? {|obj| obj.into?(c2) }
|
|
21
|
+
|
|
22
|
+
c1.connected_using?(c2, route_objs)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright © 2011 Jesse Sielaff
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
class UsaMap < MapObject
|
|
7
|
+
def build
|
|
8
|
+
atlanta, boston, calgary, charleston, chicago, dallas, denver, duluth,
|
|
9
|
+
el_paso, helena, houston, kansas_city, las_vegas, little_rock,
|
|
10
|
+
los_angeles, miami, montreal, nashville, new_orleans, new_york,
|
|
11
|
+
oklahoma_city, omaha, phoenix, pittsburgh, portland, raleigh, st_louis,
|
|
12
|
+
salt_lake_city, san_francisco, santa_fe, sault_ste_marie, seattle,
|
|
13
|
+
toronto, vancouver, washington, winnipeg = %w:
|
|
14
|
+
Atlanta Boston Calgary Charleston Chicago Dallas Denver Duluth
|
|
15
|
+
El\ Paso Helena Houston Kansas\ City Las\ Vegas Little\ Rock
|
|
16
|
+
Los\ Angeles Miami Montreal Nashville New\ Orleans New\ York
|
|
17
|
+
Oklahoma\ City Omaha Phoenix Pittsburgh Portland Raleigh St\ Louis
|
|
18
|
+
Salt\ Lake\ City San\ Francisco Santa\ Fe Sault\ Ste\ Marie Seattle
|
|
19
|
+
Toronto Vancouver Washington Winnipeg
|
|
20
|
+
:.map {|name| city(name) }
|
|
21
|
+
|
|
22
|
+
ticket(4, denver, el_paso)
|
|
23
|
+
ticket(5, kansas_city, houston)
|
|
24
|
+
ticket(6, new_york, atlanta)
|
|
25
|
+
ticket(7, calgary, salt_lake_city)
|
|
26
|
+
ticket(7, chicago, new_orleans)
|
|
27
|
+
ticket(8, duluth, houston)
|
|
28
|
+
ticket(8, helena, los_angeles)
|
|
29
|
+
ticket(8, sault_ste_marie, nashville)
|
|
30
|
+
ticket(9, sault_ste_marie, oklahoma_city)
|
|
31
|
+
ticket(9, montreal, atlanta)
|
|
32
|
+
ticket(9, seattle, los_angeles)
|
|
33
|
+
ticket(9, chicago, santa_fe)
|
|
34
|
+
ticket(10, toronto, miami)
|
|
35
|
+
ticket(10, duluth, el_paso)
|
|
36
|
+
ticket(11, winnipeg, little_rock)
|
|
37
|
+
ticket(11, dallas, new_york)
|
|
38
|
+
ticket(11, portland, phoenix)
|
|
39
|
+
ticket(11, denver, pittsburgh)
|
|
40
|
+
ticket(12, boston, miami)
|
|
41
|
+
ticket(12, winnipeg, houston)
|
|
42
|
+
ticket(13, montreal, new_orleans)
|
|
43
|
+
ticket(13, calgary, phoenix)
|
|
44
|
+
ticket(13, vancouver, santa_fe)
|
|
45
|
+
ticket(16, los_angeles, chicago)
|
|
46
|
+
ticket(17, san_francisco, atlanta)
|
|
47
|
+
ticket(17, portland, nashville)
|
|
48
|
+
ticket(20, los_angeles, miami)
|
|
49
|
+
ticket(20, vancouver, montreal)
|
|
50
|
+
ticket(21, los_angeles, new_york)
|
|
51
|
+
ticket(22, seattle, new_york)
|
|
52
|
+
|
|
53
|
+
route(denver, helena, 4, :green)
|
|
54
|
+
route(denver, kansas_city, 4, :black)
|
|
55
|
+
route(denver, kansas_city, 4, :orange)
|
|
56
|
+
route(denver, omaha, 4, :pink)
|
|
57
|
+
route(denver, oklahoma_city, 4, :red)
|
|
58
|
+
route(denver, phoenix, 5, :white)
|
|
59
|
+
route(denver, santa_fe, 2, :grey)
|
|
60
|
+
route(denver, salt_lake_city, 3, :red)
|
|
61
|
+
route(denver, salt_lake_city, 3, :yellow)
|
|
62
|
+
|
|
63
|
+
route(pittsburgh, chicago, 3, :orange)
|
|
64
|
+
route(pittsburgh, chicago, 3, :black)
|
|
65
|
+
route(pittsburgh, new_york, 2, :white)
|
|
66
|
+
route(pittsburgh, new_york, 2, :green)
|
|
67
|
+
route(pittsburgh, nashville, 4, :yellow)
|
|
68
|
+
route(pittsburgh, raleigh, 2, :grey)
|
|
69
|
+
route(pittsburgh, st_louis, 5, :green)
|
|
70
|
+
route(pittsburgh, toronto, 2, :grey)
|
|
71
|
+
route(pittsburgh, washington, 2, :grey)
|
|
72
|
+
|
|
73
|
+
route(atlanta, charleston, 2, :grey)
|
|
74
|
+
route(atlanta, miami, 5, :blue)
|
|
75
|
+
route(atlanta, nashville, 1, :grey)
|
|
76
|
+
route(atlanta, new_orleans, 4, :yellow)
|
|
77
|
+
route(atlanta, new_orleans, 4, :orange)
|
|
78
|
+
route(atlanta, raleigh, 2, :grey)
|
|
79
|
+
route(atlanta, raleigh, 2, :grey)
|
|
80
|
+
|
|
81
|
+
route(duluth, chicago, 3, :red)
|
|
82
|
+
route(duluth, helena, 6, :orange)
|
|
83
|
+
route(duluth, omaha, 2, :grey)
|
|
84
|
+
route(duluth, omaha, 2, :grey)
|
|
85
|
+
route(duluth, sault_ste_marie, 3, :grey)
|
|
86
|
+
route(duluth, toronto, 6, :pink)
|
|
87
|
+
route(duluth, winnipeg, 4, :black)
|
|
88
|
+
|
|
89
|
+
route(dallas, el_paso, 4, :red)
|
|
90
|
+
route(dallas, houston, 1, :grey)
|
|
91
|
+
route(dallas, houston, 1, :grey)
|
|
92
|
+
route(dallas, little_rock, 2, :grey)
|
|
93
|
+
route(dallas, oklahoma_city, 2, :grey)
|
|
94
|
+
route(dallas, oklahoma_city, 2, :grey)
|
|
95
|
+
|
|
96
|
+
route(kansas_city, omaha, 1, :grey)
|
|
97
|
+
route(kansas_city, omaha, 1, :grey)
|
|
98
|
+
route(kansas_city, oklahoma_city, 2, :grey)
|
|
99
|
+
route(kansas_city, oklahoma_city, 2, :grey)
|
|
100
|
+
route(kansas_city, st_louis, 2, :blue)
|
|
101
|
+
route(kansas_city, st_louis, 2, :pink)
|
|
102
|
+
|
|
103
|
+
route(san_francisco, los_angeles, 3, :yellow)
|
|
104
|
+
route(san_francisco, los_angeles, 3, :pink)
|
|
105
|
+
route(san_francisco, salt_lake_city, 5, :white)
|
|
106
|
+
route(san_francisco, salt_lake_city, 5, :orange)
|
|
107
|
+
route(san_francisco, portland, 5, :pink)
|
|
108
|
+
route(san_francisco, portland, 5, :green)
|
|
109
|
+
|
|
110
|
+
route(seattle, calgary, 4, :grey)
|
|
111
|
+
route(seattle, helena, 6, :yellow)
|
|
112
|
+
route(seattle, portland, 1, :grey)
|
|
113
|
+
route(seattle, portland, 1, :grey)
|
|
114
|
+
route(seattle, vancouver, 1, :grey)
|
|
115
|
+
route(seattle, vancouver, 1, :grey)
|
|
116
|
+
|
|
117
|
+
route(el_paso, houston, 6, :green)
|
|
118
|
+
route(el_paso, los_angeles, 6, :black)
|
|
119
|
+
route(el_paso, oklahoma_city, 5, :yellow)
|
|
120
|
+
route(el_paso, phoenix, 3, :grey)
|
|
121
|
+
route(el_paso, santa_fe, 2, :grey)
|
|
122
|
+
|
|
123
|
+
route(montreal, boston, 2, :grey)
|
|
124
|
+
route(montreal, boston, 2, :grey)
|
|
125
|
+
route(montreal, new_york, 3, :blue)
|
|
126
|
+
route(montreal, sault_ste_marie, 5, :black)
|
|
127
|
+
route(montreal, toronto, 3, :grey)
|
|
128
|
+
|
|
129
|
+
route(chicago, omaha, 4, :blue)
|
|
130
|
+
route(chicago, st_louis, 2, :green)
|
|
131
|
+
route(chicago, st_louis, 2, :white)
|
|
132
|
+
route(chicago, toronto, 4, :white)
|
|
133
|
+
|
|
134
|
+
route(helena, calgary, 4, :grey)
|
|
135
|
+
route(helena, omaha, 5, :red)
|
|
136
|
+
route(helena, salt_lake_city, 3, :pink)
|
|
137
|
+
route(helena, winnipeg, 4, :blue)
|
|
138
|
+
|
|
139
|
+
route(little_rock, nashville, 3, :white)
|
|
140
|
+
route(little_rock, new_orleans, 3, :green)
|
|
141
|
+
route(little_rock, oklahoma_city, 2, :grey)
|
|
142
|
+
route(little_rock, st_louis, 2, :grey)
|
|
143
|
+
|
|
144
|
+
route(new_york, boston, 2, :yellow)
|
|
145
|
+
route(new_york, boston, 2, :red)
|
|
146
|
+
route(new_york, washington, 2, :black)
|
|
147
|
+
route(new_york, washington, 2, :orange)
|
|
148
|
+
|
|
149
|
+
route(raleigh, charleston, 2, :grey)
|
|
150
|
+
route(raleigh, nashville, 3, :black)
|
|
151
|
+
route(raleigh, washington, 2, :grey)
|
|
152
|
+
route(raleigh, washington, 2, :grey)
|
|
153
|
+
|
|
154
|
+
route(calgary, vancouver, 3, :grey)
|
|
155
|
+
route(calgary, winnipeg, 6, :white)
|
|
156
|
+
|
|
157
|
+
route(las_vegas, los_angeles, 2, :grey)
|
|
158
|
+
route(las_vegas, salt_lake_city, 3, :orange)
|
|
159
|
+
|
|
160
|
+
route(miami, charleston, 4, :pink)
|
|
161
|
+
route(miami, new_orleans, 6, :red)
|
|
162
|
+
|
|
163
|
+
route(phoenix, los_angeles, 3, :grey)
|
|
164
|
+
route(phoenix, santa_fe, 3, :grey)
|
|
165
|
+
|
|
166
|
+
route(sault_ste_marie, toronto, 2, :grey)
|
|
167
|
+
route(sault_ste_marie, winnipeg, 6, :grey)
|
|
168
|
+
|
|
169
|
+
route(houston, new_orleans, 2, :grey)
|
|
170
|
+
|
|
171
|
+
route(nashville, st_louis, 2, :grey)
|
|
172
|
+
|
|
173
|
+
route(oklahoma_city, santa_fe, 3, :blue)
|
|
174
|
+
|
|
175
|
+
route(portland, salt_lake_city, 6, :blue)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright © 2011 Jesse Sielaff
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
class Script
|
|
7
|
+
def Script.sample (game, player)
|
|
8
|
+
until player.turn_completed?
|
|
9
|
+
case rand
|
|
10
|
+
when 0...0.01
|
|
11
|
+
player.draw_tickets
|
|
12
|
+
a, b, c = player.candidate_tickets
|
|
13
|
+
player.keep_ticket(a)
|
|
14
|
+
player.keep_ticket(b) if rand < 0.1
|
|
15
|
+
player.keep_ticket(c) if rand < 0.1
|
|
16
|
+
player.discard_tickets
|
|
17
|
+
|
|
18
|
+
when 0.01...0.1
|
|
19
|
+
game.unclaimed_routes.shuffle.any? do |route|
|
|
20
|
+
%w:black blue green orange pink red white yellow:.shuffle.any? do |color|
|
|
21
|
+
player.claim_n_wild(route, color.to_sym, rand(7))
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
when 0.1...0.2
|
|
26
|
+
player.select_card(game.available_colors.sample)
|
|
27
|
+
|
|
28
|
+
else
|
|
29
|
+
player.draw_random
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ttr
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
prerelease:
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Jesse Sielaff
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
|
|
13
|
+
date: 2011-09-26 00:00:00 Z
|
|
14
|
+
dependencies: []
|
|
15
|
+
|
|
16
|
+
description: .
|
|
17
|
+
email: jesse.sielaff@gmail.com
|
|
18
|
+
executables: []
|
|
19
|
+
|
|
20
|
+
extensions: []
|
|
21
|
+
|
|
22
|
+
extra_rdoc_files: []
|
|
23
|
+
|
|
24
|
+
files:
|
|
25
|
+
- lib/ttr/entities/city.rb
|
|
26
|
+
- lib/ttr/entities/game.rb
|
|
27
|
+
- lib/ttr/entities/opponent.rb
|
|
28
|
+
- lib/ttr/entities/player.rb
|
|
29
|
+
- lib/ttr/entities/route.rb
|
|
30
|
+
- lib/ttr/entities/ticket.rb
|
|
31
|
+
- lib/ttr/objects/card.rb
|
|
32
|
+
- lib/ttr/objects/city.rb
|
|
33
|
+
- lib/ttr/objects/colorful.rb
|
|
34
|
+
- lib/ttr/objects/deck.rb
|
|
35
|
+
- lib/ttr/objects/entity.rb
|
|
36
|
+
- lib/ttr/objects/game.rb
|
|
37
|
+
- lib/ttr/objects/map.rb
|
|
38
|
+
- lib/ttr/objects/player.rb
|
|
39
|
+
- lib/ttr/objects/route.rb
|
|
40
|
+
- lib/ttr/objects/ticket.rb
|
|
41
|
+
- lib/ttr/objects/usa_map.rb
|
|
42
|
+
- lib/ttr/sample/script.rb
|
|
43
|
+
- lib/ttr.rb
|
|
44
|
+
homepage:
|
|
45
|
+
licenses: []
|
|
46
|
+
|
|
47
|
+
post_install_message:
|
|
48
|
+
rdoc_options: []
|
|
49
|
+
|
|
50
|
+
require_paths:
|
|
51
|
+
- lib
|
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
53
|
+
none: false
|
|
54
|
+
requirements:
|
|
55
|
+
- - ">="
|
|
56
|
+
- !ruby/object:Gem::Version
|
|
57
|
+
version: 1.9.2
|
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
59
|
+
none: false
|
|
60
|
+
requirements:
|
|
61
|
+
- - ">="
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: "0"
|
|
64
|
+
requirements: []
|
|
65
|
+
|
|
66
|
+
rubyforge_project:
|
|
67
|
+
rubygems_version: 1.8.8
|
|
68
|
+
signing_key:
|
|
69
|
+
specification_version: 3
|
|
70
|
+
summary: A simulator for AI scripts to play the popular train board game.
|
|
71
|
+
test_files: []
|
|
72
|
+
|