t3_game 0.1.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 52d822ba71489b783828f73428350a253cbaf9ba
4
+ data.tar.gz: e5752f1ce4877aba626b5b04f779df881b016d3c
5
+ SHA512:
6
+ metadata.gz: 737483f6b5107398da31856899998cdd226054cf37d1a3e89918fc309a4b89b6b59aa4a0917546b10450ab2ce5b53860765ede3ae1906b97c64e5e742c8f5256
7
+ data.tar.gz: 4a45fed8f9ddd8b1af7acf6e296f8871c76cfc230c63d0cb94bd80b239d48c32cb418985090adb0b29a2a626efaa16de58312b7b1097ecb479c28a25b83807ff
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
13
+ *.gem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.3
5
+ before_install: gem install bundler -v 1.14.3
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at adrian@setyadi.pro. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in tic_tac_toe.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Adrian Setyadi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,21 @@
1
+ # TicTacToe
2
+
3
+ on your terminal.
4
+
5
+ ## Installation
6
+
7
+ $ gem install tictactoe_gem
8
+
9
+ ## Play it
10
+
11
+ $ t3
12
+
13
+ ![Play Screenshot](https://raw.githubusercontent.com/styd/TicTacToe/master/screenshot/play.gif)
14
+
15
+ ## Contributing
16
+
17
+ Bug reports and pull requests are welcome on GitHub at https://github.com/styd/TicTacToe. 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.
18
+
19
+ ## License
20
+
21
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "tic_tac_toe"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/t3 ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'tic_tac_toe'
4
+
5
+ # Take arguments from STDIN
6
+ # Examples of how this program should be called from the terminal:
7
+ # $ ./ttt.rb 3 "['x', 'o', 'v']" "['Agus', 'Budi', 'Chandra']"
8
+ # $ ./ttt.rb 4 "['x', 'o', 'v', 'w', 'y']"
9
+ # $ ./ttt.rb 6 "['x', 'o', 'v']"
10
+ # $ ./ttt.rb 5
11
+ # $ ./ttt.rb
12
+ size = ARGV.shift
13
+ symbols = ARGV.shift
14
+ players = ARGV.shift
15
+
16
+ # Let's play it
17
+ TicTacToe::Game.play size, symbols, players
@@ -0,0 +1,325 @@
1
+ ###############################################
2
+ # @author Adrian Setyadi <a_setyadi@zoho.com> #
3
+ # @game Tic Tac Toe #
4
+ ###############################################
5
+
6
+ require "tic_tac_toe/version"
7
+
8
+ module TicTacToe
9
+ STYLE = :on # Use this if your terminal supports text color
10
+ # STYLE = :off # Use this if your terminal output looks gibberish if STYLE == :on
11
+
12
+ class Player
13
+ COLOR_SET = [*(31..36)].cycle
14
+
15
+ @@num_of_registered_players = 0
16
+ @@color = COLOR_SET.each
17
+
18
+ attr_reader :name, :symbol, :color
19
+
20
+ def initialize params = {}
21
+ @@num_of_registered_players += 1
22
+ @color = @@color.next
23
+ @name = params[:name] || "Player " + @@num_of_registered_players.to_s
24
+ symbol = params[:symbol]
25
+ @symbol = STYLE == :off ? symbol : "\e[#{color}m#{symbol}\e[0m"
26
+ end
27
+
28
+ def wins
29
+ puts "#{name} wins!"
30
+ end
31
+ end
32
+
33
+ class Game
34
+ NotPlayable = Class.new(StandardError)
35
+
36
+ attr_accessor :current_player, :current_mark_pos,
37
+ :players, :play_table, :game_over,
38
+ :unique_rows, :unique_columns, :unique_diags
39
+ attr_reader :names, :symbols, :size
40
+
41
+ def initialize size, symbols, player_names
42
+ @symbols = eval(symbols || "") || ['x', 'o']
43
+ @names = eval(player_names || "") || []
44
+ @size = (size || 3).to_i
45
+
46
+ raise NotPlayable,
47
+ "Game is not playable. See examples of possible arguments in source code!"\
48
+ unless playable?
49
+ play
50
+ end
51
+
52
+ class << self
53
+ # Force to use `play` instead of `new`
54
+ alias_method :play, :new
55
+ private :new
56
+ end
57
+
58
+ def init_players
59
+ self.players = []
60
+ symbols.compact.uniq.size.times do
61
+ name = self.names.shift
62
+ symbol = self.symbols.shift
63
+ self.players << Player.new({name: name, symbol: symbol})
64
+ end
65
+ end
66
+
67
+ def playable?
68
+ size > 2 && size <= 50 &&
69
+ symbols.compact.uniq.size > 1 &&
70
+ names.compact.uniq.size <= symbols.compact.uniq.size &&
71
+ symbols.compact.uniq.all? do |m|
72
+ m.is_a?(String) && # each should be string
73
+ m.chars.count == 1 && # each should contain 1 character
74
+ m.bytes.count == 1 # each should occupy 1 byte
75
+ end
76
+ end
77
+
78
+ def wrong_input? input
79
+ message = ""
80
+ if m = input.match(/^(\d+),\s*(\d+)$/)
81
+ if m.to_a.all?{|el| el.to_i.between?(1,size) }
82
+ return false
83
+ else
84
+ message = "Input number should be between 1 and #{size}."
85
+ end
86
+ else
87
+ message = "Make sure input is in the following format: <number>,<number>"
88
+ end
89
+ return message
90
+ end
91
+
92
+ def no_longer_winnable?
93
+ (unique_rows + unique_columns + unique_diags).empty?
94
+ end
95
+
96
+ # This method will run faster every `play` loop due to the decreasing number
97
+ # of `unique_<lines>` members
98
+ def solved?
99
+ mark_row, mark_col = *current_mark_pos
100
+ symbol = current_player.symbol
101
+
102
+ # Check if solved horizontally
103
+ if unique_rows.any? and unique_rows.include? mark_row
104
+ row = play_table[mark_row]
105
+ return true if row.all?{|m| m == symbol }
106
+
107
+ # Remove row from unique list if no longer unique
108
+ unique_rows.delete(mark_row) if row.compact.uniq.size > 1
109
+ end
110
+
111
+ # Check if solved vertically
112
+ if unique_columns.any? and unique_columns.include? mark_col
113
+ column = []
114
+ size.times do |i|
115
+ column << play_table[i][mark_col]
116
+ end
117
+ return true if column.all?{|m| m == symbol }
118
+
119
+ # Remove column unique list if no longer unique
120
+ unique_columns.delete(mark_col) if column.compact.uniq.size > 1
121
+ end
122
+
123
+ # Check if solved diagonally
124
+ if (mark_row == mark_col || mark_row == size - mark_col - 1) &&
125
+ unique_diags.any?
126
+ # Initialize diagonals with negative and positive gradients
127
+ neg_diag = []
128
+ pos_diag = []
129
+
130
+ size.times do |i|
131
+ neg_diag << play_table[i][i]
132
+ pos_diag << play_table[size-i-1][i]
133
+ end
134
+ return true if neg_diag.all?{|m| m == symbol } || pos_diag.all?{|m| m == symbol }
135
+
136
+ # Remove diagonal if no longer unique
137
+ unique_diags.delete(0) if neg_diag.compact.uniq.size > 1
138
+ unique_diags.delete(1) if pos_diag.compact.uniq.size > 1
139
+ end
140
+
141
+ # Not yet solved
142
+ return false
143
+ end
144
+
145
+ def play
146
+ init_players
147
+ player = self.players.cycle.each
148
+
149
+ self.play_table = Array.new(@size){ Array.new(@size) }
150
+ self.unique_rows = [*(0...size)]
151
+ self.unique_columns = [*(0...size)]
152
+ self.unique_diags = [0, 1]
153
+
154
+ self.game_over = false
155
+ prepare_screen
156
+ until game_over do
157
+ self.current_player = player.next
158
+
159
+ pos_marked = false
160
+ until pos_marked do
161
+ refresh_screen
162
+ show_play_table
163
+ show_note
164
+ prompt
165
+ # Get input
166
+ begin
167
+ input = gets
168
+ if input.nil? # Ctrl + D pressed
169
+ raise Interrupt
170
+ else
171
+ typed_text = input.chomp.strip
172
+ end
173
+ rescue SystemExit, Interrupt # Ctrl + C pressed or program/ruby failure
174
+ forced_to_quit
175
+ end
176
+
177
+ wrong_input = wrong_input? typed_text
178
+ if wrong_input
179
+ flash wrong_input
180
+ next
181
+ end
182
+
183
+ # The row and column is 1 based
184
+ self.current_mark_pos = typed_text.split(/,\s*/).map{|c| c.to_i-1}
185
+
186
+ mark_row, mark_col = *self.current_mark_pos
187
+ symbol = self.current_player.symbol
188
+
189
+ unless play_table[mark_row][mark_col]
190
+ play_table[mark_row][mark_col] = symbol
191
+ pos_marked = true
192
+ run_some_checks
193
+ else
194
+ flash "Already marked. Choose another position!"
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ def run_some_checks
201
+ end_game if solved?
202
+ end_game :draw if no_longer_winnable?
203
+ end
204
+
205
+ def end_game symbol = nil
206
+ self.game_over = true
207
+ refresh_screen
208
+ show_play_table
209
+ puts
210
+ print "Game Over. "
211
+ if symbol == :draw
212
+ puts "It's a draw."
213
+ else
214
+ current_player.wins
215
+ end
216
+ end
217
+
218
+ def term_width
219
+ @term_width ||= `tput cols`.to_i
220
+ end
221
+
222
+ #
223
+ # Screen related methods
224
+ #
225
+ def show_note
226
+ puts dim("Type: <#{italicize("row")}>\,<#{italicize("column")}>")
227
+ puts
228
+ end
229
+
230
+ def prompt
231
+ print "\r #{colorize(current_player.name)}: "
232
+ end
233
+
234
+ def show_play_table
235
+ size_width = size.to_s.length
236
+ col_label = "COLUMN".center(3*size)
237
+ row_label = "ROW".center(size)
238
+ print " "*(size_width + 4)
239
+ print "#{italicize(col_label)}" + "\n"
240
+ print " "*(size_width + 4)
241
+ 1.upto(size) do |cnum|
242
+ print "#{boldize(cnum.to_s.center(3))}"
243
+ end
244
+ print "\n"
245
+ play_table.each.with_index(1) do |row, rnum|
246
+ print " #{italicize(row_label[rnum-1])}"
247
+ print " #{boldize(rnum.to_s.rjust(size_width))} "
248
+ row.each do |el|
249
+ if STYLE == :off
250
+ print "[#{el || " "}]"
251
+ else
252
+ print el ? "[#{el || " "}]\e[0m" : "[#{el || " "}]"
253
+ end
254
+ end
255
+ puts
256
+ end
257
+ end
258
+
259
+ def prepare_screen
260
+ (size+5).times{ puts }
261
+ end
262
+
263
+ def refresh_screen
264
+ print "\r\e[#{size+5}A"
265
+ (size+5).times do
266
+ print " " * term_width + "\n"
267
+ end
268
+ print "\r\e[#{size+5}A"
269
+ end
270
+
271
+ def forced_to_quit
272
+ puts
273
+ refresh_screen
274
+ print "Game Over. "
275
+ puts italicize("Forced to quit.")
276
+ exit
277
+ end
278
+
279
+ def colorize text
280
+ style text do
281
+ current_player.color
282
+ end
283
+ end
284
+
285
+ def italicize text
286
+ style text do
287
+ 3
288
+ end
289
+ end
290
+
291
+ def boldize text
292
+ style text do
293
+ 1
294
+ end
295
+ end
296
+
297
+ def dim text
298
+ style text do
299
+ 2
300
+ end
301
+ end
302
+
303
+ def style text
304
+ unless STYLE == :off
305
+ text = text.gsub(/\e\[0m/,"")
306
+ closing = "\e[0m"
307
+ opening_code = yield
308
+ text = "\e[#{opening_code}m#{text}#{closing}"
309
+ end
310
+ return text
311
+ end
312
+
313
+ def flash text
314
+ print "\r\e[A" + " " * term_width
315
+ prompt
316
+ if STYLE == :off
317
+ print text
318
+ else
319
+ print "\e[33m#{text}\e[0m"
320
+ end
321
+ sleep 2
322
+ puts
323
+ end
324
+ end
325
+ end
@@ -0,0 +1,3 @@
1
+ module TicTacToe
2
+ VERSION = "0.1.1"
3
+ end
Binary file
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'tic_tac_toe/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "t3_game"
8
+ spec.version = TicTacToe::VERSION
9
+ spec.authors = ["Adrian Setyadi"]
10
+ spec.email = ["a.styd@yahoo.com"]
11
+
12
+ spec.summary = %q{Tic Tac Toe on the terminal}
13
+ spec.description = %q{Tic Tac Toe on the terminal. Fun game!}
14
+ spec.homepage = "https://github.com/styd/TicTacToe"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.14"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.0"
27
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: t3_game
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Adrian Setyadi
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-03-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: Tic Tac Toe on the terminal. Fun game!
56
+ email:
57
+ - a.styd@yahoo.com
58
+ executables:
59
+ - t3
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - ".rspec"
65
+ - ".travis.yml"
66
+ - CODE_OF_CONDUCT.md
67
+ - Gemfile
68
+ - LICENSE.txt
69
+ - README.md
70
+ - Rakefile
71
+ - bin/console
72
+ - bin/setup
73
+ - exe/t3
74
+ - lib/tic_tac_toe.rb
75
+ - lib/tic_tac_toe/version.rb
76
+ - screenshot/play.gif
77
+ - t3_game.gemspec
78
+ homepage: https://github.com/styd/TicTacToe
79
+ licenses:
80
+ - MIT
81
+ metadata: {}
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubyforge_project:
98
+ rubygems_version: 2.6.10
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: Tic Tac Toe on the terminal
102
+ test_files: []