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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f437369fa63361bbb433fd5498ef7107eca3072b
4
- data.tar.gz: 5f1eda15fb4412d3e070a092cae332a0f22e8b60
3
+ metadata.gz: 3fb1feb3e76c7900e15f9700547e6a5df69d6323
4
+ data.tar.gz: 9578d3cdea1b2af011db0f36d49b3bb2be6e35e6
5
5
  SHA512:
6
- metadata.gz: dae1eb27a154ce03fc9e5c2dbe0d1930745d02556e5bb0535b3455a4ac21e10fe4afc5e680f9b4b2c7045c675531c0b27bf691b2ca040e74fb13e9e57a7bd1dd
7
- data.tar.gz: 1164df02da194b7dea1d22e6cd0ac27994b3a6fd98fa5fa2c69f7f7fff0546cf90a100fa6b5b829aa656216f54622b020cd58988730e782c6927c738e7b1af6b
6
+ metadata.gz: ccfacf294d14e754d8636dc33929ee9f5c6392d9f1360e30978a87055e620d0d98cb0cb97c63e3bf53ae7226599619a52eaa4cf07db2e2e3c5a68c608b93389f
7
+ data.tar.gz: 4069736151f29122c3cef3c0394ac7cb4b1ef93f3467a42a3aad9a001fa0ad1433a1785f0f919a27d3b83f6e756677df7f2c1186cde441c0c6d5804ced283212
data/README.md CHANGED
@@ -1 +1,257 @@
1
- Docs can be found on: https://sidofc.github.io/projects/2048/
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
+ ![Licence](https://img.shields.io/badge/license-MIT-E9573F.svg)
9
+ [![Gem Version](https://img.shields.io/gem/v/TwentyFortyEight.svg?colorB=E9573F&style=square)](rubygems.org/gems/TwentyFortyEight)
10
+ [![Issues](https://img.shields.io/github/issues/SidOfc/TwentyFortyEight.svg)](https://github.com/SidOfc/TwentyFortyEight/issues)
11
+ [![Build Status](https://img.shields.io/travis/SidOfc/TwentyFortyEight.svg)](https://travis-ci.org/SidOfc/TwentyFortyEight)
12
+ [![Coverage Status](https://img.shields.io/coveralls/SidOfc/TwentyFortyEight.svg)](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 || right || up
7
+ sequence(:left, :down) || sequence(:down, :left) ||
8
+ sequence(:right, :down) || sequence(:right, :left) ||
9
+ down || left || right || up
8
10
  end
@@ -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
@@ -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
- @@highscore = nil
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
- @@highscore ||= load_highscore
91
- @@highscore[settings.size.to_s] ||= 0
91
+ skey = settings.size.to_s
92
92
 
93
- return unless current_score > @@highscore[settings.size.to_s]
93
+ @@highscore ||= load_highscore
94
+ @@highscore[skey] ||= 0
95
+ @@session_high = current_score if current_score > @@session_high
94
96
 
95
- @@highscore[settings.size.to_s] = current_score
96
- write_highscore
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
- h[:info] << { game: (1 + game.id) } if settings.mode? :endless
125
- h[:info] << { highscore: @@highscore[settings.size.to_s], move: game.moves }
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
 
@@ -52,6 +52,10 @@ module TwentyFortyEight
52
52
  game.quit! && :quit
53
53
  end
54
54
 
55
+ def game
56
+ @game
57
+ end
58
+
55
59
  def method_missing(sym, *args, &block)
56
60
  return info sym if info? sym
57
61
  return sym if game.dup.action(sym, insert: false).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 = (options[:name] || "2048-#{Time.now.to_i}") + '.log.json'
17
- path = File.join File.expand_path(options[:path]), name if options[:path]
18
- path = File.join(Dir.pwd, name) unless options[:dir] || options[: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
- File.open(path, 'w') { |f| f.write @entries.to_json }
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
- check_file_existence! full_path
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
- check_file_existence! full_path
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
@@ -1,3 +1,3 @@
1
1
  module TwentyFortyEight
2
- VERSION = '0.2.1'
2
+ VERSION = '0.2.2'
3
3
  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.1
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-03-24 00:00:00.000000000 Z
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.8
162
+ rubygems_version: 2.6.11
163
163
  signing_key:
164
164
  specification_version: 4
165
165
  summary: A 2048 game for terminals