TwentyFortyEight 0.2.1 → 0.2.2

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