Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

making this stuff testable and simpler

  • Loading branch information...
commit 9aa4cad6fd2167a0cd03e6243ccec941c32d94ce 1 parent 6f62ec9
@grosser authored
View
8 Rakefile
@@ -1,4 +1,10 @@
-task :default do
+task :default => :spec
+require "rspec/core/rake_task"
+RSpec::Core::RakeTask.new(:spec) do |t|
+ t.rspec_opts = '--backtrace --color'
+end
+
+task :run do
exec "./bin/tic_tac_toe"
end
View
90 bin/tic_tac_toe
@@ -1,34 +1,9 @@
#!/usr/bin/env ruby
require 'curses'
-
-BOARD = <<BOARD
--------------
-| X | X | X |
--------------
-| X | X | X |
--------------
-| X | X | X |
--------------
-BOARD
-
-class String
- def indexes(needle)
- found = []
- current_index = -1
- while current_index = index(needle, current_index+1)
- found << current_index
- end
- found
- end
-end
-"aa aaaa aa".indexes('a') # [0, 1, 3, 4, 5, 6, 11, 12]
-
-def goto(x,y)
- Curses.setpos(y,x)
-end
+require File.expand_path('../../lib/tic_tac_toe', __FILE__)
def write(x,y,text)
- goto(x,y)
+ Curses.setpos(y,x)
Curses.addstr(text);
end
@@ -43,61 +18,20 @@ def init_screen
end
end
-def draw_board(fields)
- index = -1
- board = BOARD.gsub('X') do
- index += 1
- fields[index]
- end
- write(0,0, board)
-end
-
-def move_player(x,y)
- # set new $position
- current = possible_positions.index($position)
- x = (current + x) % 3
- y = ((current / 3) + y) % 3
- $position = x + (y * 3)
- write(0,0,$position.inspect)
-
- # set the cursor
- x,y = possible_positions[$position]
- write x,y,'O'
-end
-
-def possible_positions
- board_width = BOARD.split("\n").first.size + 1
- BOARD.indexes('X').map do |index|
- x = index % board_width
- y = index / board_width
- [x, y]
- end
-end
-
-def all_indices_of(haystack, needle)
- indexes = []
- offset = 0
- while index = BOARD.index('X', offset)
- x = index % board_width
- y = index / board_width
- positions << [x, y]
- offset = index
- end
-end
-
init_screen do
- $position = possible_positions.first
- fields = [' ',' ',' ',' ',' ',' ',' ',' ',' ']
- draw_board fields
+ ttt = TicTacToe.new{|board| write(0,0,board) }
loop do
- case Curses.getch
- when Curses::Key::UP then move_player(0,1)
- when Curses::Key::DOWN then move_player(0,-1)
- when Curses::Key::RIGHT then move_player(1,0)
- when Curses::Key::LEFT then move_player(-1,0)
+ char = Curses.getch
+ case char
+ when Curses::Key::UP then ttt.move(0,-1)
+ when Curses::Key::DOWN then ttt.move(0,1)
+ when Curses::Key::RIGHT then ttt.move(1,0)
+ when Curses::Key::LEFT then ttt.move(-1,0)
+ when 10 then ttt.set # enter
when ?q then break
+ else
+ # write(0, 20, char.to_s) # debugging
end
- draw_board fields
end
end
View
72 lib/tic_tac_toe.rb
@@ -0,0 +1,72 @@
+class String
+ def indexes(needle)
+ found = []
+ current_index = -1
+ while current_index = index(needle, current_index+1)
+ found << current_index
+ end
+ found
+ end
+end
+
+class TicTacToe
+ VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
+
+ BOARD = <<BOARD
+-------------
+| X | X | X |
+-------------
+| X | X | X |
+-------------
+| X | X | X |
+-------------
+BOARD
+ PLAYERS = ['X','O']
+
+ attr_reader :position, :player
+
+ def initialize(&block)
+ @board = BOARD
+ @fields = Array.new(9).fill(' ')
+ @position = 0
+ @observer = block
+ @player = PLAYERS.first
+ updated!
+ end
+
+ def updated!
+ @observer.call board
+ end
+
+ def board
+ index = -1
+ BOARD.gsub(" X ") do
+ index += 1
+ field = @fields[index]
+ @position == index ? "[#{field}]" : " #{field} "
+ end
+ end
+
+ def move(x,y)
+ x = (@position + x) % 3
+ y = ((@position / 3) + y) % 3
+ @position = x + (y * 3)
+
+ updated!
+ end
+
+ def set
+ return if @fields[@position] != ' '
+ @fields[@position] = @player
+ switch_player
+ updated!
+ end
+
+ private
+
+ def switch_player
+ current_player = PLAYERS.index(@player)
+ next_player = (current_player + 1) % PLAYERS.size
+ @player = PLAYERS[next_player]
+ end
+end
View
2  spec/spec_helper.rb
@@ -0,0 +1,2 @@
+$LOAD_PATH.unshift 'lib'
+require 'tic_tac_toe'
View
84 spec/tic_tac_toe_spec.rb
@@ -0,0 +1,84 @@
+require File.expand_path('spec/spec_helper')
+
+describe TicTacToe do
+ board_width = 14
+
+ it "has a VERSION" do
+ TicTacToe::VERSION.should =~ /^\d+\.\d+\.\d+$/
+ end
+
+ describe 'on startup' do
+ let(:initial_board) do
+ board = nil
+ TicTacToe.new{|b| board = b }
+ board
+ end
+
+ it "should draw an empty board on startup" do
+ initial_board.should_not include('X')
+ initial_board.should_not include('O')
+ initial_board.size.should == board_width * 7
+ end
+
+ it "should position the cursor in the first position" do
+ initial_board.indexes('[ ]').should == [board_width+1]
+ end
+ end
+
+ describe :move do
+ {
+ [0,0] => 0,
+ [1,0] => 1,
+ [3,0] => 0,
+ [-1,0] => 2,
+ [0,1] => 3,
+ [0,3] => 0,
+ [0,-1] => 6,
+ }.each do |move,position|
+ it "is at position #{position} when moving from origin to #{move}" do
+ ttt = TicTacToe.new{}
+ ttt.move(*move)
+ ttt.position.should == position
+ end
+ end
+
+ it "updates the cursor" do
+ board = nil
+ ttt = TicTacToe.new{|b| board = b }
+ ttt.move(1,1)
+ board.index('[ ]').should == (board_width*3) + 5
+ end
+ end
+
+ describe :set do
+ before do
+ @ttt = TicTacToe.new{|b| @board = b }
+ end
+
+ it "marks the field for the current player" do
+ @ttt.set
+ @board.index('[X]').should == board_width + 1
+ end
+
+ it "switches the player" do
+ @ttt.player.should == 'X'
+ @ttt.set
+ @ttt.player.should == 'O'
+ @ttt.move(1,0)
+ @ttt.set
+ @ttt.player.should == 'X'
+ end
+
+ it "does not set an occupied field" do
+ @ttt.set
+ @ttt.set
+ @board.index('[O]').should == nil
+ end
+
+ it "does not switch players when setting an occupied field" do
+ @ttt.set
+ @ttt.set
+ @ttt.player.should == 'O'
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.