TwentyFortyEight 0.2.1 → 0.2.2
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.
- checksums.yaml +4 -4
- data/README.md +257 -1
- data/bin/2048 +3 -1
- data/bin/console +0 -4
- data/lib/TwentyFortyEight.rb +18 -9
- data/lib/TwentyFortyEight/dsl.rb +4 -0
- data/lib/TwentyFortyEight/logger.rb +9 -14
- data/lib/TwentyFortyEight/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3fb1feb3e76c7900e15f9700547e6a5df69d6323
|
4
|
+
data.tar.gz: 9578d3cdea1b2af011db0f36d49b3bb2be6e35e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ccfacf294d14e754d8636dc33929ee9f5c6392d9f1360e30978a87055e620d0d98cb0cb97c63e3bf53ae7226599619a52eaa4cf07db2e2e3c5a68c608b93389f
|
7
|
+
data.tar.gz: 4069736151f29122c3cef3c0394ac7cb4b1ef93f3467a42a3aad9a001fa0ad1433a1785f0f919a27d3b83f6e756677df7f2c1186cde441c0c6d5804ced283212
|
data/README.md
CHANGED
@@ -1 +1,257 @@
|
|
1
|
-
|
1
|
+
# TwentyFortyEight
|
2
|
+
Play a game of 2048 in your terminal, there are options that allow you to do various things such as log the moves of a game using `-l`, letting it auto-play endlessly using mode `endless`, and optionally record history using `-h` (only useful in `endless` mode).
|
3
|
+
|
4
|
+
The game is played using either the **arrow keys**, **W**, **A**, **S**, **D** or the vim keys: **K**, **H**, **J**, **L**. After a game has ended, you will be prompted to press either **Q** or **R**. pressing **Q** exits the command completely.
|
5
|
+
|
6
|
+
## Status
|
7
|
+
|
8
|
+

|
9
|
+
[](rubygems.org/gems/TwentyFortyEight)
|
10
|
+
[](https://github.com/SidOfc/TwentyFortyEight/issues)
|
11
|
+
[](https://travis-ci.org/SidOfc/TwentyFortyEight)
|
12
|
+
[](https://coveralls.io/github/SidOfc/TwentyFortyEight?branch=master)
|
13
|
+
|
14
|
+
---
|
15
|
+
|
16
|
+
## Changelog
|
17
|
+
|
18
|
+
_dates are in dd-mm-yyyy format_
|
19
|
+
|
20
|
+
#### 08-12-2017 VERSION 0.2.2
|
21
|
+
|
22
|
+
- Logger no longer throws exceptions if no `logs` dir exists in the current working directory.
|
23
|
+
|
24
|
+
#### 24-03-2017 VERSION 0.2.1
|
25
|
+
|
26
|
+
- Fixed `TwentyFortyEight::Game#won?` method, it no longer causes an exception
|
27
|
+
- Added tests for core components
|
28
|
+
- Updated some gemspec info
|
29
|
+
- Added config files for travis CI and Coveralls
|
30
|
+
- Added more defaults
|
31
|
+
- `TwentyFortyEight::Board` now has `{ size: 4, fill: 0, empty: 0 }` as defaults
|
32
|
+
- `TwentyFortyEight::Cli` now has the _history_ option enabled by default in _endless_ mode
|
33
|
+
- `TwentyFortyEight::Logger` can now `load!` an existing log file
|
34
|
+
- Added `sequence` method to `TwentyFortyEight::Dsl` which allows you to either execute a sequence of moves completely, or not at all
|
35
|
+
|
36
|
+
#### 22-03-2017 VERSION 0.2.0
|
37
|
+
|
38
|
+
- Highscore is stored in a file
|
39
|
+
- each fieldsize has it's own highscore
|
40
|
+
- Added vim keykinds
|
41
|
+
- Added keybinds to the `--help` flag
|
42
|
+
- Only 'relevant' info is shown in a specified game mode
|
43
|
+
- Removed display of 'id' from the 'play' mode
|
44
|
+
- Removed solver option completely
|
45
|
+
|
46
|
+
## Installation
|
47
|
+
|
48
|
+
Add this line to your application's Gemfile:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
gem 'TwentyFortyEight'
|
52
|
+
```
|
53
|
+
|
54
|
+
And then execute:
|
55
|
+
|
56
|
+
```
|
57
|
+
$ bundle
|
58
|
+
```
|
59
|
+
|
60
|
+
Or install it yourself as:
|
61
|
+
|
62
|
+
```
|
63
|
+
$ gem install TwentyFortyEight
|
64
|
+
```
|
65
|
+
|
66
|
+
## Usage
|
67
|
+
|
68
|
+
The game will store a highscore for each field size played. This means that playing a 4x4 will show a different highscore then playing a 5x5. The file in which this is stored can be found at `~/.2048`, it will contain these scores in the JSON format.
|
69
|
+
|
70
|
+
### From the commandline
|
71
|
+
|
72
|
+
To simply play a game:
|
73
|
+
|
74
|
+
```
|
75
|
+
$ 2048
|
76
|
+
```
|
77
|
+
|
78
|
+
To let it automatically play using the predefined order: `:left`, `:down`, `:right`, `:up`
|
79
|
+
|
80
|
+
```
|
81
|
+
$ 2048 play
|
82
|
+
```
|
83
|
+
|
84
|
+
Need help?
|
85
|
+
|
86
|
+
```
|
87
|
+
$ 2048 --help
|
88
|
+
```
|
89
|
+
|
90
|
+
### General
|
91
|
+
|
92
|
+
After installing the gem globally or in your application you'll have to `require` the gem before being able to use it.
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
require 'TwentyFortyEight'
|
96
|
+
```
|
97
|
+
|
98
|
+
When the gem is loaded, you can proceed to call one of the following methods:
|
99
|
+
|
100
|
+
* `TwentyFortyEight#play options = { size: 4, fill: 0, empty: 0 }, &block`
|
101
|
+
* `TwentyFortyEight#endless options = { size: 4, fill: 0, empty: 0 }, &block`
|
102
|
+
|
103
|
+
The options can be a hash containing any of the options available to the CLI.
|
104
|
+
This means you can set the following keys:
|
105
|
+
|
106
|
+
* `:interactive` - A `Boolean` indicating wether or not to play the game manually (thereby printing the game as well regardless of `:verbose`)
|
107
|
+
* `:exclude` - An `Array` of directions to exclude by default when playing the game. Once the remaining moves become unavailable, all moves will be allowed for use to get unstuck
|
108
|
+
* `:only` - The opposite of `:exclude`
|
109
|
+
* `:delay` - An `Integer` representing a delay in milliseconds to `sleep` between each move
|
110
|
+
* `:size` - An `Integer` representing the size of the board, default 4
|
111
|
+
* `:log` - A `Boolean` indicating wether or not to save a log file to `./logs`
|
112
|
+
* `:history` - A `Boolean` indicating wether or not to show a game history log next to the game
|
113
|
+
* `:verbose` - A `Boolean` indicating wether or not to display the game, overridden when `:interactive` is `true`
|
114
|
+
|
115
|
+
When the game ends, the `TwentyFortyEight::Game` instance will be returned.
|
116
|
+
This object contains all the information there is about the game, it will also contain an accessible _log_ if `:log` is set to `true`.
|
117
|
+
|
118
|
+
The returned object has the following useful calls:
|
119
|
+
|
120
|
+
* `Game#won?` - Returns a `Boolean` result of wether the highest tile was `> 2048`
|
121
|
+
* `Game#lost?` - Opposite of `Game#won?`
|
122
|
+
* `Game#end?` - Returns a `Boolean` which tells you if the game has properly ended (unmergeable and board full)
|
123
|
+
* `Game#log` - Returns an instance of `TwentyFortyEight::Logger`, a simple wrapper class containing an array of entries accessible through `Logger#entries`
|
124
|
+
* `Game#score` - Returns an `Integer` representing the final score of the game
|
125
|
+
|
126
|
+
### Connect your script!
|
127
|
+
|
128
|
+
You can also connect your script to the game. A block can also be passed to `TwentyFortyEight#play` and `TwentyFortyEight#endless`.
|
129
|
+
Within that block, all you need to do is return a direction. There is a small DSL within that allows you to do moves based on availability.
|
130
|
+
With that, you can create simple patterns, e.g:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
require 'TwentyFortyEight'
|
134
|
+
|
135
|
+
game = TwentyFortyEight.play log: true do
|
136
|
+
left || down || right || up
|
137
|
+
end
|
138
|
+
|
139
|
+
puts game.score # => 2345
|
140
|
+
puts game.log.entries # => [{... log: 'entry' ...}, [{...}, {...}]]
|
141
|
+
puts game.won? # => false
|
142
|
+
puts game.end? # => true
|
143
|
+
```
|
144
|
+
|
145
|
+
### The DSL
|
146
|
+
|
147
|
+
A small DSL built into the game allows for easier creation of patterns. A small example:
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
require 'TwentyFortyEight'
|
151
|
+
|
152
|
+
game = TwentyFortyEight.play do
|
153
|
+
left || down || right || up
|
154
|
+
end
|
155
|
+
```
|
156
|
+
|
157
|
+
What the above does is copy the current state of the game, execute the move on the copy and return the direction if the board changed or nothing (`nil`) otherwise. The `||` statements will continue to the next expression until something returns truthy. If no move is possible the game will simply end.
|
158
|
+
|
159
|
+
The problem with this setup is that there is no real way of adding complex patterns, you can basically only chain those 4 in different orders to eventually create a maximum of 16 patterns.
|
160
|
+
|
161
|
+
#### Helpers
|
162
|
+
|
163
|
+
Below is a list of helpers available within the _block_ passed to the game:
|
164
|
+
|
165
|
+
| helper | description |
|
166
|
+
|-----------------------|-----------------------------------------------|
|
167
|
+
|`left` | returns `:left` if possible, otherwise `nil` |
|
168
|
+
|`right` | returns `:right` if possible, otherwise `nil` |
|
169
|
+
|`up` | returns `:up` if possible, otherwise `nil` |
|
170
|
+
|`down` | returns `:down` if possible, otherwise `nil` |
|
171
|
+
|`sequence(*dir_syms)` | attempts to execute each move consecutively, if all change the game the pattern will be executed else `nil` is returned. |
|
172
|
+
|`available` | an `Array` of `Hash`es, each containing `:x` and `:y` keys that represent an empty tile in the current board. |
|
173
|
+
|`score` | the current score |
|
174
|
+
|`prev_score` | the previous score (before last move was executed) |
|
175
|
+
|`changed?` | wether the board changed since last turn (the move helpers (`sequence`, `left`, `right`, `up` and `down`) do this for you) |
|
176
|
+
|`won?` | wether you beat the game by achieving the `2048` tile |
|
177
|
+
|`lost?` | you'll have lost if you can no longer merge and the board is full |
|
178
|
+
|`quit!` | force quit the game |
|
179
|
+
|
180
|
+
#### Code examples
|
181
|
+
|
182
|
+
The following will give you some insight on how to get started and what goes on in the background.
|
183
|
+
|
184
|
+
##### Overview
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
require 'TwentyFortyEight'
|
188
|
+
|
189
|
+
game = TwentyFortyEight.play do
|
190
|
+
left # => :left or nil
|
191
|
+
right # => :right or nil
|
192
|
+
up # => :up or nil
|
193
|
+
down # => :right or nil
|
194
|
+
sequence :down, :left # => the next direction until the end has been reached
|
195
|
+
available # => [{ x: 0, y: 0 }, { x: 1, y: 1 }, ...]
|
196
|
+
score # => 2222
|
197
|
+
won? # => true or false
|
198
|
+
changed? # => true or false
|
199
|
+
lost? # => true or false
|
200
|
+
quit! # => :quit
|
201
|
+
end
|
202
|
+
```
|
203
|
+
|
204
|
+
##### Simple pattern
|
205
|
+
|
206
|
+
executes in order:
|
207
|
+
|
208
|
+
1. `:left` else
|
209
|
+
2. `:down` else
|
210
|
+
3. `:right` else
|
211
|
+
4. `:up`
|
212
|
+
|
213
|
+
```ruby
|
214
|
+
require 'TwentyFortyEight'
|
215
|
+
|
216
|
+
game = TwentyFortyEight.play do
|
217
|
+
left || down || right || up
|
218
|
+
end
|
219
|
+
```
|
220
|
+
|
221
|
+
#### Sequences
|
222
|
+
|
223
|
+
executes in order:
|
224
|
+
|
225
|
+
1. `:down` twice, `:left` twice else
|
226
|
+
2. `:down` and `:left` else
|
227
|
+
3. `:down` and `:right` else
|
228
|
+
4. `:right` and `:left` else
|
229
|
+
5. `:left` else
|
230
|
+
6. `:down` else
|
231
|
+
7. `:right` else
|
232
|
+
8. `:up`
|
233
|
+
|
234
|
+
```ruby
|
235
|
+
require 'TwentyFortyEight'
|
236
|
+
|
237
|
+
game = TwentyFortyEight.play do
|
238
|
+
sequence(:down, :down, :left, :left) ||
|
239
|
+
sequence(:down, :left) ||
|
240
|
+
sequence(:down, :right) ||
|
241
|
+
sequence(:right, :left) ||
|
242
|
+
left || down || right || up # => to not get stuck
|
243
|
+
end
|
244
|
+
```
|
245
|
+
|
246
|
+
## Roadmap
|
247
|
+
|
248
|
+
* Improve logging options
|
249
|
+
|
250
|
+
## Contributing
|
251
|
+
|
252
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Sidney Liebrand/TwentyFortyEight. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
253
|
+
|
254
|
+
|
255
|
+
## License
|
256
|
+
|
257
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/bin/2048
CHANGED
@@ -4,5 +4,7 @@ require_relative '../lib/TwentyFortyEight'
|
|
4
4
|
options = TwentyFortyEight::Cli.parse!
|
5
5
|
|
6
6
|
TwentyFortyEight.send options.mode, options do
|
7
|
-
down || left ||
|
7
|
+
sequence(:left, :down) || sequence(:down, :left) ||
|
8
|
+
sequence(:right, :down) || sequence(:right, :left) ||
|
9
|
+
down || left || right || up
|
8
10
|
end
|
data/bin/console
CHANGED
@@ -6,9 +6,5 @@ require 'TwentyFortyEight'
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
8
8
|
|
9
|
-
@game = TwentyFortyEight.endless verbose: true do
|
10
|
-
down || left || right || up
|
11
|
-
end
|
12
|
-
|
13
9
|
require 'pry'
|
14
10
|
Pry.start
|
data/lib/TwentyFortyEight.rb
CHANGED
@@ -14,8 +14,9 @@ module TwentyFortyEight
|
|
14
14
|
MODES = [:play, :endless].freeze
|
15
15
|
SETTINGS = { size: 4, fill: 0, empty: 0 }.freeze
|
16
16
|
|
17
|
-
@@games
|
18
|
-
@@
|
17
|
+
@@games = []
|
18
|
+
@@session_high = 0
|
19
|
+
@@highscore = nil
|
19
20
|
|
20
21
|
def self.play(settings = {}, &block)
|
21
22
|
settings = Options.new SETTINGS.merge(settings)
|
@@ -87,13 +88,16 @@ module TwentyFortyEight
|
|
87
88
|
end
|
88
89
|
|
89
90
|
def self.load_or_set_highscore!(current_score, settings, path = '~/.2048')
|
90
|
-
|
91
|
-
@@highscore[settings.size.to_s] ||= 0
|
91
|
+
skey = settings.size.to_s
|
92
92
|
|
93
|
-
|
93
|
+
@@highscore ||= load_highscore
|
94
|
+
@@highscore[skey] ||= 0
|
95
|
+
@@session_high = current_score if current_score > @@session_high
|
94
96
|
|
95
|
-
@@highscore[
|
96
|
-
|
97
|
+
return unless current_score > @@highscore[skey]
|
98
|
+
|
99
|
+
@@highscore[skey] = current_score
|
100
|
+
write_highscore path
|
97
101
|
end
|
98
102
|
|
99
103
|
def self.write_highscore(path = '~/.2048')
|
@@ -120,9 +124,14 @@ module TwentyFortyEight
|
|
120
124
|
|
121
125
|
def self.render_game(game, settings, final = false)
|
122
126
|
h = { interactive: settings.interactive?, info: [] }
|
127
|
+
s = settings.key.to_s
|
123
128
|
|
124
|
-
|
125
|
-
|
129
|
+
if settings.mode? :endless
|
130
|
+
h[:info] << { highscore: "#{@@session_high}/#{@@highscore[settings.size.to_s]}" }
|
131
|
+
h[:info] << { game: (1 + game.id), move: game.moves }
|
132
|
+
else
|
133
|
+
h[:info] << { highscore: @@highscore[settings.size.to_s] , move: game.moves}
|
134
|
+
end
|
126
135
|
h[:info] << { score: game.score, dir: game.current_dir}
|
127
136
|
h[:history] = (@@games + [game]) if settings.history?
|
128
137
|
|
data/lib/TwentyFortyEight/dsl.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module TwentyFortyEight
|
3
4
|
# Logger
|
4
5
|
class Logger
|
@@ -13,31 +14,25 @@ module TwentyFortyEight
|
|
13
14
|
end
|
14
15
|
|
15
16
|
def write!(options = {})
|
16
|
-
name
|
17
|
-
path
|
18
|
-
path
|
19
|
-
path = (File.join Dir.pwd, options[:dir], name) if options[:dir]
|
17
|
+
name = (options[:name] || "2048-#{Time.now.to_i}") + '.log.json'
|
18
|
+
path = options[:path] || options[:dir] || Dir.pwd
|
19
|
+
path = File.expand_path('./' + path) unless path.start_with? '/'
|
20
20
|
|
21
|
-
|
21
|
+
return unless Dir.exist? path
|
22
|
+
File.open(File.join(path, name), 'w') { |f| f.write @entries.to_json }
|
22
23
|
end
|
23
24
|
|
24
25
|
def self.load!(path)
|
25
26
|
full_path = File.expand_path path
|
26
|
-
|
27
|
-
|
27
|
+
return unless File.exist? full_path
|
28
28
|
new JSON.parse(File.read(full_path), symbolize_names: true)
|
29
29
|
end
|
30
30
|
|
31
31
|
def self.destroy!(path)
|
32
32
|
full_path = File.expand_path path
|
33
|
-
|
33
|
+
return unless File.exist? full_path
|
34
34
|
File.delete full_path
|
35
|
+
true
|
35
36
|
end
|
36
|
-
|
37
|
-
def self.check_file_existence!(path)
|
38
|
-
raise FileNotFound, 'Log does not exist' unless File.exist? path
|
39
|
-
end
|
40
|
-
|
41
|
-
class FileNotFound < StandardError; end
|
42
37
|
end
|
43
38
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: TwentyFortyEight
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sidney Liebrand
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-12-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -159,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
159
159
|
version: '0'
|
160
160
|
requirements: []
|
161
161
|
rubyforge_project:
|
162
|
-
rubygems_version: 2.6.
|
162
|
+
rubygems_version: 2.6.11
|
163
163
|
signing_key:
|
164
164
|
specification_version: 4
|
165
165
|
summary: A 2048 game for terminals
|