TwentyFortyEight
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).
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.
Status
Changelog
dates are in dd-mm-yyyy format
08-12-2017 VERSION 0.2.2
- Logger no longer throws exceptions if no
logsdir exists in the current working directory.
24-03-2017 VERSION 0.2.1
- Fixed
TwentyFortyEight::Game#won?method, it no longer causes an exception - Added tests for core components
- Updated some gemspec info
- Added config files for travis CI and Coveralls
- Added more defaults
TwentyFortyEight::Boardnow has{ size: 4, fill: 0, empty: 0 }as defaultsTwentyFortyEight::Clinow has the history option enabled by default in endless mode
TwentyFortyEight::Loggercan nowload!an existing log file- Added
sequencemethod toTwentyFortyEight::Dslwhich allows you to either execute a sequence of moves completely, or not at all
22-03-2017 VERSION 0.2.0
- Highscore is stored in a file
- each fieldsize has it's own highscore
- Added vim keykinds
- Added keybinds to the
--helpflag
- Added keybinds to the
- Only 'relevant' info is shown in a specified game mode
- Removed display of 'id' from the 'play' mode
- Removed solver option completely
Installation
Add this line to your application's Gemfile:
gem 'TwentyFortyEight'
And then execute:
$ bundle
Or install it yourself as:
$ gem install TwentyFortyEight
Usage
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.
From the commandline
To simply play a game:
$ 2048
To let it automatically play using the predefined order: :left, :down, :right, :up
$ 2048 play
Need help?
$ 2048 --help
General
After installing the gem globally or in your application you'll have to require the gem before being able to use it.
require 'TwentyFortyEight'
When the gem is loaded, you can proceed to call one of the following methods:
TwentyFortyEight#play options = { size: 4, fill: 0, empty: 0 }, &blockTwentyFortyEight#endless options = { size: 4, fill: 0, empty: 0 }, &block
The options can be a hash containing any of the options available to the CLI. This means you can set the following keys:
:interactive- ABooleanindicating wether or not to play the game manually (thereby printing the game as well regardless of:verbose):exclude- AnArrayof 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:only- The opposite of:exclude:delay- AnIntegerrepresenting a delay in milliseconds tosleepbetween each move:size- AnIntegerrepresenting the size of the board, default 4:log- ABooleanindicating wether or not to save a log file to./logs:history- ABooleanindicating wether or not to show a game history log next to the game:verbose- ABooleanindicating wether or not to display the game, overridden when:interactiveistrue
When the game ends, the TwentyFortyEight::Game instance will be returned.
This object contains all the information there is about the game, it will also contain an accessible log if :log is set to true.
The returned object has the following useful calls:
Game#won?- Returns aBooleanresult of wether the highest tile was> 2048Game#lost?- Opposite ofGame#won?Game#end?- Returns aBooleanwhich tells you if the game has properly ended (unmergeable and board full)Game#log- Returns an instance ofTwentyFortyEight::Logger, a simple wrapper class containing an array of entries accessible throughLogger#entriesGame#score- Returns anIntegerrepresenting the final score of the game
Connect your script!
You can also connect your script to the game. A block can also be passed to TwentyFortyEight#play and TwentyFortyEight#endless.
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.
With that, you can create simple patterns, e.g:
require 'TwentyFortyEight'
game = TwentyFortyEight.play log: true do
left || down || right || up
end
puts game.score # => 2345
puts game.log.entries # => [{... log: 'entry' ...}, [{...}, {...}]]
puts game.won? # => false
puts game.end? # => true
The DSL
A small DSL built into the game allows for easier creation of patterns. A small example:
require 'TwentyFortyEight'
game = TwentyFortyEight.play do
left || down || right || up
end
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.
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.
Helpers
Below is a list of helpers available within the block passed to the game:
| helper | description |
|---|---|
left |
returns :left if possible, otherwise nil |
right |
returns :right if possible, otherwise nil |
up |
returns :up if possible, otherwise nil |
down |
returns :down if possible, otherwise nil |
sequence(*dir_syms) |
attempts to execute each move consecutively, if all change the game the pattern will be executed else nil is returned. |
available |
an Array of Hashes, each containing :x and :y keys that represent an empty tile in the current board. |
score |
the current score |
prev_score |
the previous score (before last move was executed) |
changed? |
wether the board changed since last turn (the move helpers (sequence, left, right, up and down) do this for you) |
won? |
wether you beat the game by achieving the 2048 tile |
lost? |
you'll have lost if you can no longer merge and the board is full |
quit! |
force quit the game |
Code examples
The following will give you some insight on how to get started and what goes on in the background.
Overview
require 'TwentyFortyEight'
game = TwentyFortyEight.play do
left # => :left or nil
right # => :right or nil
up # => :up or nil
down # => :right or nil
sequence :down, :left # => the next direction until the end has been reached
available # => [{ x: 0, y: 0 }, { x: 1, y: 1 }, ...]
score # => 2222
won? # => true or false
changed? # => true or false
lost? # => true or false
quit! # => :quit
end
Simple pattern
executes in order:
:leftelse:downelse:rightelse:up
require 'TwentyFortyEight'
game = TwentyFortyEight.play do
left || down || right || up
end
Sequences
executes in order:
:downtwice,:lefttwice else:downand:leftelse:downand:rightelse:rightand:leftelse:leftelse:downelse:rightelse:up
require 'TwentyFortyEight'
game = TwentyFortyEight.play do
sequence(:down, :down, :left, :left) ||
sequence(:down, :left) ||
sequence(:down, :right) ||
sequence(:right, :left) ||
left || down || right || up # => to not get stuck
end
Roadmap
- Improve logging options
Contributing
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 code of conduct.
License
The gem is available as open source under the terms of the MIT License.