rchess 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'pry'
4
+ gem 'pry-nav'
5
+ # Specify your gem's dependencies in rchess.gemspec
6
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 gregory
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # Rchess
2
+
3
+ Just a personnal challenge to code a chess game [as fast as possible](https://github.com/gregory/rchess/graphs/punch-card)
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rchess'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rchess
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,6 @@
1
+ require 'wisper'
2
+ Dir[File.dirname(__FILE__) + '/rchess/**/*.rb'].each{ |file| require file }
3
+
4
+ module Rchess
5
+ # Your code goes here...
6
+ end
@@ -0,0 +1,95 @@
1
+ module Rchess
2
+ class Board
3
+ include Wisper::Publisher
4
+
5
+ BOUDARIES = (0...8)
6
+ EMPTY_BOX = ""
7
+
8
+ attr_reader :loosed_pieces
9
+ attr_writer :boxes
10
+
11
+ def initialize
12
+ @boxes =[[:R, :C, :B, :Q, :K, :B, :C, :R],
13
+ [:P, :P, :P, :P, :P, :P, :P, :P],
14
+ ["", "", "", "", "", "", "", ""],
15
+ ["", "", "", "", "", "", "", ""],
16
+ ["", "", "", "", "", "", "", ""],
17
+ ["", "", "", "", "", "", "", ""],
18
+ [:p, :p, :p, :p, :p, :p, :p, :p],
19
+ [:r, :c, :b, :q, :k, :b, :c, :r]]
20
+ end
21
+
22
+ def loosed_pieces
23
+ @loosed_pieces ||= { white: [], black: [] }
24
+ end
25
+
26
+ def boxes
27
+ @boxes ||= Array.new(BOUDARIES.count){ Array.new(BOUDARIES.count){ EMPTY_BOX }}
28
+ end
29
+
30
+ def movement_within_board?(srcCoord, dstCoord)
31
+ coord_within_boundaries?(srcCoord) && coord_within_boundaries?(dstCoord)
32
+ end
33
+
34
+ def piece_at_coord(coord)
35
+ box_at_coord(coord) == EMPTY_BOX ? nil : Piece.new({coord: coord, board: self})
36
+ end
37
+
38
+ def box_at_coord(coord)
39
+ @boxes[coord.y][coord.x]
40
+ end
41
+
42
+ def move_src_to_dst!(srcCoord, dstCoord)
43
+ srcPiece = piece_at_coord(srcCoord)
44
+ dstPiece = piece_at_coord(dstCoord)
45
+ src_box_value = dstPiece.nil? || dstPiece.color != srcPiece.color ? EMPTY_BOX : dstPiece.type # if diff color or nil we erase, else we swap
46
+
47
+ @boxes[srcPiece.y][srcPiece.x], @boxes[dstCoord.y][dstCoord.x] = src_box_value, srcPiece.type
48
+ end
49
+
50
+ def coord_is_threatened_by_color?(target_coord, color)
51
+ binding.pry
52
+ paths_from_target = Paths::Base.threaten_destinations_from_coord(target_coord, self)
53
+
54
+ paths_from_target.flatten(1).select{|p| !p.empty?}.any? do |paths|
55
+ paths.any? do |coord|
56
+ dstPiece = self.piece_at_coord(coord)
57
+ dstPiece && dstPiece.color != color
58
+ end
59
+ end
60
+ end
61
+
62
+ def valid_move?(piece, dstCoord)
63
+ piece.can_goto_coord?(dstCoord) && !king_would_be_threatened?(piece, dstCoord)
64
+ end
65
+
66
+ def coord_for_type_and_color(type, color)
67
+ piece_type = Piece.type_to_color(type, color) #TODO: make the type dynamic ~> piece::TYPES['king']
68
+ piece_index = self.boxes.flatten.index(piece_type)
69
+ coord_from_index(piece_index)
70
+ end
71
+
72
+ private
73
+
74
+ def king_would_be_threatened?(piece, dstCoord)
75
+ #simulate the next board
76
+ next_board = Board.new
77
+ #TODO: need a more efficient way of copying the values
78
+ next_board.boxes = Marshal.load(Marshal.dump(@boxes))
79
+ next_board.move_src_to_dst!(piece.coord, dstCoord)
80
+
81
+ king_coord = coord_for_type_and_color(:k, piece.color)
82
+ oponent_color = piece.color == Piece::WHITE_COLOR ? Piece::BLACK_COLOR : Piece::WHITE_COLOR
83
+ coord_is_threatened_by_color?(king_coord, oponent_color)
84
+ end
85
+
86
+ def coord_within_boundaries?(coord)
87
+ BOUDARIES.include?(coord.x) && BOUDARIES.include?(coord.y)
88
+ end
89
+
90
+ def coord_from_index(index)
91
+ width = BOUDARIES.count
92
+ Coord.new({y: index/width, x: index%width})
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,18 @@
1
+ module Rchess
2
+ class Coord
3
+ attr_accessor :x, :y
4
+
5
+ def initialize(h)
6
+ self.x = h.fetch(:x){ raise ArgumentError.new("Please provide a value for key :x") }
7
+ self.y = h.fetch(:y){ raise ArgumentError.new("Please provide a value for key :y") }
8
+ end
9
+
10
+ def apply_delta(delta)
11
+ Coord.new({ x: (self.x + delta[:x]), y: (self.y + delta[:y]) })
12
+ end
13
+
14
+ def to_hash
15
+ { x: self.x, y: self.y }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,49 @@
1
+ module Rchess
2
+ class Game
3
+ attr_accessor :board
4
+ attr_reader :current_player_color, :loosed_pieces
5
+
6
+ def initialize
7
+ @current_player_color = Piece::WHITE_COLOR
8
+ @loosed_pieces = {Piece::WHITE_COLOR => [], Piece::BLACK_COLOR => []}
9
+ end
10
+
11
+ def add_loosed_piece(dstPiece)
12
+ self.loosed_pieces[dstPiece.color] << dstPiece.type
13
+ end
14
+
15
+ def move!(srcCoord, dstCoord)
16
+ return false unless board.movement_within_board?(srcCoord, dstCoord)
17
+
18
+ piece = board.piece_at_coord(srcCoord)
19
+ return false unless piece
20
+ return false unless current_player_own_piece?(piece)
21
+ return false unless board.valid_move?(piece, dstCoord)
22
+
23
+ dstPiece = board.piece_at_coord(dstCoord)
24
+ add_loosed_piece(dstPiece) if dstPiece
25
+ board.move_src_to_dst!(piece, dstCoord)
26
+ switch_current_player
27
+ !checked?
28
+ end
29
+
30
+ def checked?
31
+ king = board.king_for_color(current_player_color)
32
+ king.is_threaten?
33
+ end
34
+
35
+ def board
36
+ @board ||= Board.new
37
+ end
38
+
39
+ private
40
+
41
+ def switch_current_player
42
+ @current_player_color = @current_player_color == Piece::WHITE_COLOR ? Piece::BLACK_COLOR : Piece::WHITE_COLOR
43
+ end
44
+
45
+ def current_player_own_piece?(piece)
46
+ current_player_color == piece.color
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,42 @@
1
+ module Rchess
2
+ module Paths
3
+ class Base
4
+ attr_reader :coord
5
+ attr_accessor :board
6
+
7
+ def initialize(params)
8
+ @coord = params.fetch(:coord)
9
+ @board = params.fetch(:board)
10
+ end
11
+
12
+ def srcBox
13
+ @srcBox ||= @bord.box_at_coord(@coord)
14
+ end
15
+
16
+ def srcDirection
17
+ @srcDirection ||= srcBox.downcase == srcBox ? -1 : 1 #:up or :down
18
+ end
19
+
20
+ def destinations
21
+ @destinations ||= self.paths.map{ |path| apply_delta_to_path(path).delete_if{ |coord| coord.x < 0 || coord.y < 0 } }
22
+ end
23
+
24
+ def paths
25
+ raise NotImplementedError.new("You must implement paths")
26
+ end
27
+
28
+ def self.threaten_destinations_from_coord(coord, board)
29
+ params = { coord: coord, board: board }
30
+ [
31
+ Paths::Pawn.new(params).destinations, Paths::Bishop.new(params).destinations, Paths::Knight.new(params).destinations, Paths::Rook.new(params).destinations
32
+ ].flatten(1)
33
+ end
34
+
35
+ private
36
+
37
+ def apply_delta_to_path(path)
38
+ path.map{ |delta| self.coord.apply_delta(delta) }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,27 @@
1
+ module Rchess
2
+ module Paths
3
+ class Bishop < Base
4
+ def initialize(params)
5
+ @power = params.fetch(:power, 8)
6
+ super(params)
7
+ end
8
+
9
+ def paths
10
+ [diag_paths].flatten(1).select do |path|
11
+ path.select
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def diag_paths
18
+ [
19
+ (1..@power).map{ |i| { x: i, y: i } },
20
+ (1..@power).map{ |i| { x: i, y: -i } },
21
+ (1..@power).map{ |i| { x: -i, y: i } },
22
+ (1..@power).map{ |i| { x: -i, y: -i } }
23
+ ]
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ module Rchess
2
+ module Paths
3
+ class King < Base
4
+ def paths
5
+ [basic_paths, rock_paths].flatten(1)
6
+ end
7
+
8
+ private
9
+
10
+ def rock_paths
11
+ [{x: -3, y: 0}, {x: 2, y: 0}].select do |delta|
12
+ dstCoord = self.coord.apply_delta(delta)
13
+ oponent_color = self.color == Piece::WHITE_COLOR ? Piece::BLACK_COLOR : Piece::WHITE_COLOR
14
+ !self.board.coord_is_threatened_by_color?(dstCoord, oponent_color)
15
+ end
16
+ end
17
+
18
+ def basic_paths
19
+ [Bishop.new({coord: @coord, board: @board, power: 1}), Rook.new({coord: @coord, board: @board, power: 1})]
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ module Rchess
2
+ module Paths
3
+ class Knight < Base
4
+ def paths
5
+ [l_paths].flatten(1)
6
+ end
7
+
8
+ private
9
+
10
+ def l_paths
11
+ [
12
+ [{ x: 1, y: 2 }],
13
+ [{ x: -1, y: 2 }],
14
+ [{ x: 1, y: -2 }],
15
+ [{ x: -1, y: -2 }],
16
+ [{ x: 2, y: 1 }],
17
+ [{ x: -2, y: 1 }],
18
+ [{ x: 2, y: -1 }],
19
+ [{ x: -2, y: -1 }]
20
+ ]
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,41 @@
1
+ module Rchess
2
+ module Paths
3
+ class Pawn < Base
4
+ def paths
5
+ [move_forward, take_piece, move_2_if_first_move].select{|path| !path.empty?}
6
+ end
7
+
8
+ private
9
+
10
+ def move_2_if_first_move
11
+ if piece_first_move?
12
+ [{x: 0, y: 2*srcDirection}, {x: 0, y: 2*srcDirection}]
13
+ else
14
+ []
15
+ end
16
+ end
17
+
18
+ def piece_first_move?
19
+ self.coord.y == 1 && self.srcDirection == 1 || self.coord.y == 6 && self.srcDirection == -1
20
+ end
21
+
22
+ def take_piece
23
+ #TODO: remove the color notion, there is only 1 piece that goes up and one that goes down
24
+ [{x: 1, y: direction}, {x: -1, y: srcDirection}].select do |delta|
25
+ dstCoord = self.coord.apply_delta(delta)
26
+ dstBox = self.board.box_at_coord(dstCoord)
27
+ self.board.direction_for_coord(coord) #TODO: implement - talk about boxes not pieces
28
+ !dstPiece.nil? && dstPiece.direction != srcPiece.direction
29
+ end
30
+ end
31
+
32
+ def move_forward
33
+ [{x: 0, y: direction}].select do |delta|
34
+ dstCoord = self.coord.apply_delta(delta)
35
+ dstPiece = self.board.piece_at_coord(dstCoord)
36
+ dstPiece.nil?
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,9 @@
1
+ module Rchess
2
+ module Paths
3
+ class Queen < Base
4
+ def paths
5
+ [Bishop.new({coord: @coord, board: @board}), Rook.new({coord: @coord, board: @board})].flatten(1)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,25 @@
1
+ module Rchess
2
+ module Paths
3
+ class Rook < Base
4
+ def initialize(params)
5
+ @power = params.fetch(:power, 8)
6
+ super(params)
7
+ end
8
+
9
+ def paths
10
+ [linear_paths].flatten(1)
11
+ end
12
+
13
+ private
14
+
15
+ def linear_paths
16
+ [
17
+ (1..@power).map{ |i| { x: 0, y: i } },
18
+ (1..@power).map{ |i| { x: 0, y: -i } },
19
+ (1..@power).map{ |i| { x: i, y: 0 } },
20
+ (1..@power).map{ |i| { x: -i, y: 0 } }
21
+ ]
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,68 @@
1
+ require 'forwardable'
2
+ module Rchess
3
+ class Piece
4
+ extend Forwardable
5
+
6
+ TYPES={
7
+ p: ->(coord, board){ Paths::Pawn.new( {coord: coord, board: board}) },
8
+ b: ->(coord, board){ Paths::Bishop.new({coord: coord, board: board}) },
9
+ q: ->(coord, board){ Paths::Queen.new( {coord: coord, board: board}) },
10
+ c: ->(coord, board){ Paths::Knight.new({coord: coord, board: board}) },
11
+ r: ->(coord, board){ Paths::Rook.new( {coord: coord, board: board}) },
12
+ k: ->(coord, baord){ Paths::King.new( {coord: coord, board: board}) }
13
+ }
14
+
15
+ WHITE_COLOR = :white
16
+ BLACK_COLOR = :black
17
+
18
+ COLORS={
19
+ WHITE_COLOR => :downcase,
20
+ BLACK_COLOR => :upcase
21
+ }
22
+
23
+ def_delegators :@coord, :x, :y
24
+ def_delegators :paths, :destinations
25
+
26
+ attr_reader :coord
27
+
28
+ def initialize(options)
29
+ @board = options.fetch(:board) #The environment
30
+ @coord = options.fetch(:coord) #The position
31
+ end
32
+
33
+ def self.type_to_color(type, color)
34
+ raise ArgumentError.new("Unknown color #{color}") unless COLORS.keys.include? color
35
+ type.send(COLORS[color])
36
+ end
37
+
38
+ def color
39
+ return @color if @color
40
+
41
+ cases = COLORS.invert
42
+ self.type.downcase == self.type ? cases[:downcase] : cases[:upcase]
43
+ end
44
+
45
+ def direction
46
+ #TODO: make it more dynamic
47
+ self.color == WHITE_COLOR ? :up : :down
48
+ end
49
+
50
+ def type
51
+ @type ||= @board.box_at_coord(@coord)
52
+ end
53
+
54
+ def can_goto_coord?(coord)
55
+ destinations.flatten.any?{|dest| dest.x == coord.x && dest.y == coord.y }
56
+ end
57
+
58
+ def is_threaten?
59
+ false #TODO: implement
60
+ end
61
+
62
+ private
63
+
64
+ def paths
65
+ @paths ||= TYPES[self.type.downcase].call(@coord, @board)
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,3 @@
1
+ module Rchess
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rchess/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rchess"
8
+ spec.version = Rchess::VERSION
9
+ spec.authors = ["gregory"]
10
+ spec.email = ["greg2502@gmail.com"]
11
+ spec.description = %q{A ruby chess game}
12
+ spec.summary = %q{no need for summary, go check the source}
13
+ spec.homepage = "http://github.com/gregory/rchess"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "wisper"
24
+
25
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rchess
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - gregory
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-01-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ version_requirements: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ none: false
21
+ name: bundler
22
+ type: :development
23
+ prerelease: false
24
+ requirement: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ version: '1.3'
29
+ none: false
30
+ - !ruby/object:Gem::Dependency
31
+ version_requirements: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ! '>='
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ none: false
37
+ name: rake
38
+ type: :development
39
+ prerelease: false
40
+ requirement: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ none: false
46
+ - !ruby/object:Gem::Dependency
47
+ version_requirements: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ none: false
53
+ name: wisper
54
+ type: :development
55
+ prerelease: false
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ none: false
62
+ description: A ruby chess game
63
+ email:
64
+ - greg2502@gmail.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - Gemfile
71
+ - LICENSE.txt
72
+ - README.md
73
+ - Rakefile
74
+ - lib/rchess.rb
75
+ - lib/rchess/board.rb
76
+ - lib/rchess/coord.rb
77
+ - lib/rchess/game.rb
78
+ - lib/rchess/path.rb
79
+ - lib/rchess/paths/bishop.rb
80
+ - lib/rchess/paths/king.rb
81
+ - lib/rchess/paths/knight.rb
82
+ - lib/rchess/paths/pawn.rb
83
+ - lib/rchess/paths/queen.rb
84
+ - lib/rchess/paths/rook.rb
85
+ - lib/rchess/piece.rb
86
+ - lib/rchess/version.rb
87
+ - rchess.gemspec
88
+ homepage: http://github.com/gregory/rchess
89
+ licenses:
90
+ - MIT
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ none: false
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ none: false
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 1.8.23
110
+ signing_key:
111
+ specification_version: 3
112
+ summary: no need for summary, go check the source
113
+ test_files: []
114
+ has_rdoc: