ttr 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|