stack_wars 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +661 -0
- data/README.md +70 -0
- data/RULES.txt +113 -0
- data/bin/stack_wars +63 -0
- data/example/sample-moves.txt +190 -0
- data/example/sample_game.rb +31 -0
- data/lib/stack_wars.rb +10 -0
- data/lib/stack_wars/battlefield.rb +65 -0
- data/lib/stack_wars/errors.rb +7 -0
- data/lib/stack_wars/game.rb +85 -0
- data/lib/stack_wars/player.rb +20 -0
- data/lib/stack_wars/territory.rb +75 -0
- data/lib/stack_wars/text_client.rb +25 -0
- data/lib/stack_wars/text_display.rb +34 -0
- data/lib/stack_wars/version.rb +3 -0
- data/test/fixtures/active_battlefield.json +9 -0
- data/test/fixtures/empty_battlefield.json +9 -0
- data/test/fixtures/moves.json +13 -0
- data/test/integration/full_game_test.rb +32 -0
- data/test/suite.rb +2 -0
- data/test/test_helper.rb +11 -0
- data/test/units/territory_test.rb +107 -0
- data/test/units/text_client_test.rb +27 -0
- metadata +80 -0
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
Stack Wars is an abstract board game currently being developed by Gregory Brown
|
2
|
+
(@seacreature) and Jia Wu (@sindhri)
|
3
|
+
|
4
|
+
The game itself can be played on Chess or Go board as long as you have some
|
5
|
+
stackable objects available to you (coins or bingo markers work fine). However,
|
6
|
+
the goal of this project is to build a computerized version so that anyone can
|
7
|
+
play without any special equipment.
|
8
|
+
|
9
|
+
See the RULES.txt file for an explanation of how the game works, and
|
10
|
+
follow the instructions below.
|
11
|
+
|
12
|
+
## To try out the game
|
13
|
+
|
14
|
+
First install the `stack_wars` Ruby gem
|
15
|
+
|
16
|
+
$ gem install stack_wars
|
17
|
+
|
18
|
+
Next, step through a sample game to get a sense of how things work
|
19
|
+
|
20
|
+
$ stack_wars demo
|
21
|
+
|
22
|
+
If the demo made sense, start up your own game. Make sure to have a friend
|
23
|
+
to share the keyboard with, as there are no bots yet. Rudimentary online
|
24
|
+
games can be done via tmux or screen, but only if you already know
|
25
|
+
how to use those tools and have a shared server handy ;)
|
26
|
+
|
27
|
+
$ stack_wars
|
28
|
+
|
29
|
+
If the demo didn't make sense, you can review the rules once more:
|
30
|
+
(uses the `less` command line tool)
|
31
|
+
|
32
|
+
$ stack_wars rules
|
33
|
+
|
34
|
+
If things still don't make sense, you may want to wait until this project is
|
35
|
+
farther along before playing with it further. But I'm happy to answer whatever
|
36
|
+
questions you might have: gregory.t.brown@gmail.com
|
37
|
+
|
38
|
+
_Please note that right now this game has a very rudimentary command line
|
39
|
+
interface and has not been tested on any platforms other than Ruby 1.9.2 /
|
40
|
+
Mac OS X 10.6. If you are trying this game out now, please make sure you
|
41
|
+
feel comfortable diving into the source code if things don't work as
|
42
|
+
expected._
|
43
|
+
|
44
|
+
## To contribute to this project
|
45
|
+
|
46
|
+
There is a lot to be done on this project, and I'd be happy to accept contributions.
|
47
|
+
If you'd like to help out, here's where to start:
|
48
|
+
|
49
|
+
0) Make sure you've got a working install of Ruby 1.9.2 handy
|
50
|
+
1) Fork the project on github: https://github.com/sandal/stack_wars
|
51
|
+
2) Install Bundler if necessary (gem install bundler) [*]
|
52
|
+
3) Run bundle install [*]
|
53
|
+
4) Run ruby tests/suite.rb and verify tests are passing
|
54
|
+
5) Run ruby examples/sample_game.rb and make sure it works as expected
|
55
|
+
|
56
|
+
[*] Feel free to skip bundler instructions if you know what you're doing and
|
57
|
+
would rather not use Bundler, there are no runtime dependencies on it.
|
58
|
+
|
59
|
+
Once you have gone through those steps, please email me at
|
60
|
+
gregory.t.brown@gmail.com letting me know that you'd like to contribute to the
|
61
|
+
project. If you had any trouble with the setup process, you can let me know at
|
62
|
+
that time.
|
63
|
+
|
64
|
+
I will eventually post a roadmap publicly, but it's a bit early for that now. If
|
65
|
+
you get in touch with me, I'll work with you to figure out what you can work on
|
66
|
+
based on your interests and the needs of the project.
|
67
|
+
|
68
|
+
_NOTE: Contributors retain copyright to their work but must agree to release their
|
69
|
+
contributions under the [GNU Affero GPL version
|
70
|
+
3](http://www.gnu.org/licenses/agpl.html)_
|
data/RULES.txt
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
Stack Wars is an abstract board game played on a two dimensional grid, similar
|
2
|
+
to a Chess or Go board. The game is currently still being play tested /
|
3
|
+
balanced, but the rules below should give you enough to try it out on your own.
|
4
|
+
|
5
|
+
== Board layout + setup
|
6
|
+
|
7
|
+
The board represents a battlefield that two players must
|
8
|
+
fight for control over, with the goal of invading each other's bases. The ASCII
|
9
|
+
representation below shows a standard 9x9 grid representing the battlefield.
|
10
|
+
|
11
|
+
0 1 2 3 4 5 6 7 8
|
12
|
+
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
|
13
|
+
0 (___)--(___)--(___)--(___)--(___)--(___)--(___)--(___)--(___)
|
14
|
+
| | | | | | | | |
|
15
|
+
1 (___)--(___)--(___)--(___)--(___)--(___)--(___)--(___)--(___)
|
16
|
+
| | | | | | | | |
|
17
|
+
2 (___)--(___)--(___)--(___)--(___)--(___)--(___)--(___)--(___)
|
18
|
+
| | | | | | | | |
|
19
|
+
3 (___)--(___)--(___)--(___)--(___)--(___)--(___)--(___)--(___)
|
20
|
+
| | | | | | | | |
|
21
|
+
4 (___)--(___)--(___)--(___)--(___)--(___)--(___)--(___)--(___)
|
22
|
+
| | | | | | | | |
|
23
|
+
5 (___)--(___)--(___)--(___)--(___)--(___)--(___)--(___)--(___)
|
24
|
+
| | | | | | | | |
|
25
|
+
6 (___)--(___)--(___)--(___)--(___)--(___)--(___)--(___)--(___)
|
26
|
+
| | | | | | | | |
|
27
|
+
7 (___)--(___)--(___)--(___)--(___)--(___)--(___)--(___)--(___)
|
28
|
+
| | | | | | | | |
|
29
|
+
8 (___)--(___)--(___)--(___)--(___)--(___)--(___)--(___)--(___)
|
30
|
+
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
31
|
+
|
32
|
+
The battlefield starts off empty (as shown above), with each player holding a
|
33
|
+
number of armies in reserve equal to three times the width of the battlefield.
|
34
|
+
For example, on a 9x9 grid, each player starts off with 27 armies, on a 13x13
|
35
|
+
each player starts off with 39 armies, and so on.
|
36
|
+
|
37
|
+
== Possible turn actions
|
38
|
+
|
39
|
+
Black starts the game, and then play alternates as each player takes one of the
|
40
|
+
following actions per turn.
|
41
|
+
|
42
|
+
- FORTIFY a CONTROLLED territory by adding a single army from reserves
|
43
|
+
- MOVE a single army from a CONTROLLED territory to an adjacent
|
44
|
+
UNCLAIMED territory
|
45
|
+
- ATTACK a single enemy territory adjacent to one of the player's
|
46
|
+
CONTROLLED territories
|
47
|
+
- INVADE the enemy base (may be a MOVE or ATTACK, details explained later)
|
48
|
+
|
49
|
+
Throughout the game, the following territories are considered to be CONTROLLED
|
50
|
+
by the active player:
|
51
|
+
|
52
|
+
1) Anything that lies on the BASELINE (i.e. boundary line between the
|
53
|
+
battlefield and the player's base). This works out to any territory
|
54
|
+
in row 0 for white, and any territory in row N for black, where N is
|
55
|
+
the last row of the board.
|
56
|
+
|
57
|
+
2) Any territory which has at least one of the active player's army fortified
|
58
|
+
within it.
|
59
|
+
|
60
|
+
Some restrictions apply to how turns play out, depending on game conditions:
|
61
|
+
|
62
|
+
* In order to FORTIFY a position, the active player must have at least one
|
63
|
+
army in reserves.
|
64
|
+
|
65
|
+
* Adjacent territories are a single edge away horizontally or vertically,
|
66
|
+
but not diagonally. E.g. adjacent territories to (2,2) are:
|
67
|
+
(2,1), (2,3), (1,2), (3,2). You cannot MOVE, ATTACK, or INVADE diagonally
|
68
|
+
at any point in time.
|
69
|
+
|
70
|
+
* Two or more armies within a single territory form a STACK. If moving from
|
71
|
+
a STACK to another position, only a single army is moved, not the
|
72
|
+
entire STACK.
|
73
|
+
|
74
|
+
* You can only carry out an ATTACK from a territory which does not contain
|
75
|
+
a STACK (i.e. a position with a single army)
|
76
|
+
|
77
|
+
* If an ATTACK on an enemy territory causes that territory to becomes
|
78
|
+
unoccupied, you must immediately move the army you attacked with
|
79
|
+
into that territory.
|
80
|
+
|
81
|
+
* Whenever an army is removed from the battlefield via an ATTACK,
|
82
|
+
it is immediately removed from play, and does not count for or against
|
83
|
+
either player's score, nor does it get returned to the either player's
|
84
|
+
reserves.
|
85
|
+
|
86
|
+
== Gaining Points
|
87
|
+
|
88
|
+
An INVASION is carried out by moving one of the active player's armies into an
|
89
|
+
adjacent territory which is part of the enemy's BASELINE. This can be done via a
|
90
|
+
MOVE or an ATTACK. As soon as the active player's army is moved onto the
|
91
|
+
baseline, it is removed from play, and the active player gets one point added to
|
92
|
+
their score.
|
93
|
+
|
94
|
+
== Ending the game
|
95
|
+
|
96
|
+
At the exact moment that one player has no armies in play as well as no armies
|
97
|
+
in reserve, the game immediately ends. If one player has more points than the
|
98
|
+
other, the player with the greater amount of points. If instead each player
|
99
|
+
has an equal number of points, the game ends in a draw.
|
100
|
+
|
101
|
+
Alteratively, both players have the option of resigning at any time. Because
|
102
|
+
this is a game in which it may be obvious who the winner is well ahead of the
|
103
|
+
standard end game conditions, it should not be considered impolite to resign
|
104
|
+
when the battle is no longer interesting.
|
105
|
+
|
106
|
+
== Questions / Suggestions?
|
107
|
+
|
108
|
+
Email gregory.t.brown@gmail.com
|
109
|
+
|
110
|
+
== Implementation
|
111
|
+
|
112
|
+
You can find the source code for this game at:
|
113
|
+
http://github.com/sandal/stack_wars
|
data/bin/stack_wars
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# NOTE: This script is just a proof of concept.
|
4
|
+
# This game will eventually have a GUI of some sort
|
5
|
+
|
6
|
+
require_relative "../lib/stack_wars"
|
7
|
+
|
8
|
+
def play_game
|
9
|
+
field = StackWars::Battlefield.new
|
10
|
+
game = StackWars::Game.new(field)
|
11
|
+
|
12
|
+
client = StackWars::TextClient.new(game)
|
13
|
+
|
14
|
+
message = catch(:game_over) do
|
15
|
+
loop do
|
16
|
+
system "clear"
|
17
|
+
puts field
|
18
|
+
|
19
|
+
puts
|
20
|
+
puts "Current Score: #{game.active_player.color}: "+
|
21
|
+
"#{game.active_player.successful_invasions}, "+
|
22
|
+
"#{game.opponent.color}: "+
|
23
|
+
"#{game.opponent.successful_invasions}"
|
24
|
+
|
25
|
+
print "#{game.active_player.color}:#{game.active_player.reserves} > "
|
26
|
+
|
27
|
+
begin
|
28
|
+
move = yield
|
29
|
+
|
30
|
+
if move[/resign/i]
|
31
|
+
throw :game_over, "#{game.active_player.color} resigned"
|
32
|
+
else
|
33
|
+
client.play(move)
|
34
|
+
end
|
35
|
+
rescue StackWars::Errors::ParseError
|
36
|
+
puts "invalid command, press enter to continue"
|
37
|
+
gets
|
38
|
+
rescue StackWars::Errors::IllegalMove
|
39
|
+
puts "that move is illegal, press enter to continue"
|
40
|
+
gets
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
puts field
|
46
|
+
puts message
|
47
|
+
end
|
48
|
+
|
49
|
+
case ARGV[0]
|
50
|
+
when "rules"
|
51
|
+
system("less #{File.dirname(__FILE__)}/../RULES.txt")
|
52
|
+
when "demo"
|
53
|
+
file = "#{File.dirname(__FILE__)}/../example/sample-moves.txt"
|
54
|
+
moves = File.foreach(file)
|
55
|
+
play_game do
|
56
|
+
move = moves.next
|
57
|
+
puts "#{move}\nhit enter to continue"
|
58
|
+
$stdin.gets
|
59
|
+
move
|
60
|
+
end
|
61
|
+
else
|
62
|
+
play_game { $stdin.gets }
|
63
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
3 8
|
2
|
+
6 0
|
3
|
+
6 8
|
4
|
+
6 0 6 1
|
5
|
+
6 8 6 7
|
6
|
+
6 1 6 2
|
7
|
+
6 7 6 6
|
8
|
+
6 2 6 3
|
9
|
+
6 6 6 5
|
10
|
+
6 3
|
11
|
+
6 5
|
12
|
+
6 3 6 4
|
13
|
+
6 5 5 5
|
14
|
+
6 4 7 4
|
15
|
+
6 5 6 6
|
16
|
+
7 4 7 5
|
17
|
+
5 5
|
18
|
+
7 5
|
19
|
+
5 5 5 6
|
20
|
+
7 5 7 6
|
21
|
+
7 8
|
22
|
+
6 3 7 3
|
23
|
+
8 8
|
24
|
+
7 3 7 4
|
25
|
+
8 8
|
26
|
+
7 4 8 4
|
27
|
+
8 8 8 7
|
28
|
+
8 4 8 5
|
29
|
+
5 5 5 4
|
30
|
+
8 5
|
31
|
+
7 8 7 7
|
32
|
+
8 5 8 6
|
33
|
+
7 7 7 6
|
34
|
+
8 6 7 6
|
35
|
+
5 4 5 3
|
36
|
+
8 5
|
37
|
+
5 3 5 2
|
38
|
+
8 5 8 6
|
39
|
+
5 2
|
40
|
+
7 6 6 6
|
41
|
+
5 2 5 1
|
42
|
+
6 6 5 6
|
43
|
+
4 8
|
44
|
+
7 5
|
45
|
+
5 8
|
46
|
+
7 5 7 6
|
47
|
+
5 2
|
48
|
+
7 6
|
49
|
+
8 7 7 7
|
50
|
+
7 6 6 6
|
51
|
+
7 8
|
52
|
+
6 6
|
53
|
+
6 8
|
54
|
+
5 6
|
55
|
+
5 8 5 7
|
56
|
+
5 6 4 6
|
57
|
+
5 8
|
58
|
+
4 6
|
59
|
+
5 1 5 0
|
60
|
+
4 6 3 6
|
61
|
+
5 2 5 1
|
62
|
+
3 6
|
63
|
+
5 2
|
64
|
+
3 6 3 7
|
65
|
+
5 1 5 0
|
66
|
+
3 7 3 8
|
67
|
+
3 8
|
68
|
+
3 6
|
69
|
+
5 8 5 7
|
70
|
+
3 6 3 7
|
71
|
+
5 7 5 8
|
72
|
+
3 7 3 8
|
73
|
+
3 8
|
74
|
+
5 0
|
75
|
+
6 8 6 7
|
76
|
+
7 6 7 7
|
77
|
+
7 8 7 7
|
78
|
+
5 6 5 7
|
79
|
+
5 8 5 7
|
80
|
+
6 6 5 6
|
81
|
+
5 7 5 6
|
82
|
+
6 6 5 6
|
83
|
+
5 2 4 2
|
84
|
+
4 6
|
85
|
+
4 8 4 7
|
86
|
+
4 6 3 6
|
87
|
+
4 7 4 6
|
88
|
+
5 6 4 6
|
89
|
+
3 8 3 7
|
90
|
+
3 6 2 6
|
91
|
+
4 2 4 1
|
92
|
+
3 6 3 7
|
93
|
+
3 8
|
94
|
+
2 6 2 7
|
95
|
+
3 8 3 7
|
96
|
+
2 7 3 7
|
97
|
+
4 8
|
98
|
+
4 6
|
99
|
+
6 7 5 7
|
100
|
+
4 6 5 6
|
101
|
+
5 8
|
102
|
+
3 7
|
103
|
+
3 8
|
104
|
+
3 7 3 6
|
105
|
+
5 2
|
106
|
+
8 6
|
107
|
+
5 2 4 2
|
108
|
+
8 6 7 6
|
109
|
+
7 8
|
110
|
+
3 6
|
111
|
+
3 8 3 7
|
112
|
+
3 6 2 6
|
113
|
+
3 7 3 6
|
114
|
+
2 6 3 6
|
115
|
+
3 8
|
116
|
+
7 5
|
117
|
+
4 2
|
118
|
+
7 5 6 5
|
119
|
+
4 1 4 0
|
120
|
+
4 0
|
121
|
+
4 2 4 1
|
122
|
+
4 0 4 1
|
123
|
+
4 2 4 1
|
124
|
+
6 5 6 6
|
125
|
+
6 8
|
126
|
+
6 6
|
127
|
+
6 8 6 7
|
128
|
+
6 6 6 5
|
129
|
+
5 2 5 1
|
130
|
+
5 0 5 1
|
131
|
+
4 1 5 1
|
132
|
+
3 6
|
133
|
+
4 8 4 7
|
134
|
+
4 6 4 7
|
135
|
+
5 7 4 7
|
136
|
+
3 6 2 6
|
137
|
+
2 8
|
138
|
+
2 6
|
139
|
+
2 8 2 7
|
140
|
+
2 6 1 6
|
141
|
+
2 7 2 6
|
142
|
+
1 6 2 6
|
143
|
+
3 8 3 7
|
144
|
+
3 6 2 6
|
145
|
+
3 7 2 7
|
146
|
+
2 6 1 6
|
147
|
+
2 7 2 6
|
148
|
+
1 6 2 6
|
149
|
+
4 7 3 7
|
150
|
+
2 6
|
151
|
+
3 7 2 7
|
152
|
+
2 6 1 6
|
153
|
+
2 7 2 6
|
154
|
+
1 6 2 6
|
155
|
+
5 8 5 7
|
156
|
+
2 6 2 7
|
157
|
+
5 7 4 7
|
158
|
+
2 7
|
159
|
+
4 7 3 7
|
160
|
+
2 7 1 7
|
161
|
+
3 7 2 7
|
162
|
+
1 7 2 7
|
163
|
+
7 8 6 8
|
164
|
+
2 7
|
165
|
+
6 8 5 8
|
166
|
+
2 7
|
167
|
+
5 8 4 8
|
168
|
+
2 7 2 8
|
169
|
+
4 8 3 8
|
170
|
+
2 7 2 8
|
171
|
+
5 1 5 0
|
172
|
+
7 6 7 7
|
173
|
+
6 7 7 7
|
174
|
+
2 7 2 8
|
175
|
+
3 8 3 7
|
176
|
+
5 6 5 7
|
177
|
+
3 7 3 6
|
178
|
+
5 7 5 8
|
179
|
+
3 6 4 6
|
180
|
+
7 5 7 6
|
181
|
+
7 7 7 8
|
182
|
+
6 6 6 7
|
183
|
+
4 6 5 6
|
184
|
+
6 5 6 6
|
185
|
+
5 6 6 6
|
186
|
+
7 6 6 6
|
187
|
+
7 8 7 7
|
188
|
+
6 7 7 7
|
189
|
+
8 8 8 7
|
190
|
+
8 6 8 7
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative "../lib/stack_wars"
|
2
|
+
|
3
|
+
field = StackWars::Battlefield.new
|
4
|
+
game = StackWars::Game.new(field)
|
5
|
+
moves = File.foreach("#{File.dirname(__FILE__)}/sample-moves.txt")
|
6
|
+
|
7
|
+
client = StackWars::TextClient.new(game)
|
8
|
+
|
9
|
+
message = catch(:game_over) do
|
10
|
+
loop do
|
11
|
+
system "clear"
|
12
|
+
puts field
|
13
|
+
|
14
|
+
puts
|
15
|
+
puts "Current Score: #{game.active_player.color}: "+
|
16
|
+
"#{game.active_player.successful_invasions}, "+
|
17
|
+
"#{game.opponent.color}: "+
|
18
|
+
"#{game.opponent.successful_invasions}"
|
19
|
+
|
20
|
+
command = moves.next
|
21
|
+
print "#{game.active_player.color}:#{game.active_player.reserves} > #{command}"
|
22
|
+
client.play(command)
|
23
|
+
|
24
|
+
|
25
|
+
puts "hit enter to continue"
|
26
|
+
gets
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
puts field
|
31
|
+
puts message
|