Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for promoting pawns #110

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/controllers/pieces_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ def valid_moves
render json: selected_piece.valid_moves
end

def promote
render(text: 'Piece is not promotable', status: :forbidden) && return unless selected_piece.promotable?
selected_piece.promote_to!(params[:type])
redirect_to current_game
end

private

def authorize_player
Expand Down
4 changes: 4 additions & 0 deletions app/models/chess_piece.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ def load_potential_moves
potential_moves
end

def promotable?
false
end

private

def move_for(coordinates)
Expand Down
16 changes: 15 additions & 1 deletion app/models/pawn.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,25 @@ def move_to!(coordinates)
two_step_move = diff_in_y(coordinates.y) == FIRST_MOVE_FACTOR
capture_en_passant(coordinates) if game.can_en_passant?(coordinates)
super
game.update(en_passant_position: "#{coordinates.x},#{backward_one(coordinates.y)}") if two_step_move
update_en_passant(coordinates) if two_step_move
game.update_current_player!(opposite_color) if promotable?
end

def promotable?
position_y == 8 || position_y == 1
end

def promote_to!(type)
update(type: type)
game.update_current_player!(color)
end

private

def update_en_passant(coordinates)
game.update(en_passant_position: "#{coordinates.x},#{backward_one(coordinates.y)}")
end

def capture_en_passant(coordinates)
game.chess_pieces.find_by(
position_x: coordinates.x,
Expand Down
5 changes: 4 additions & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
root 'static_pages#index'
resources :pieces, only: [:show, :update] do
get 'valid_moves', on: :member
member do
get 'valid_moves'
put 'promote'
end
end
resources :users, only: [:show, :update] do
get '/username', to: 'users#username'
Expand Down
35 changes: 21 additions & 14 deletions spec/controllers/pieces_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -1,34 +1,30 @@
require 'rails_helper'

RSpec.describe PiecesController, type: :controller do
describe 'GET pieces#show' do
before do
@black_player = create(:user)
@game = create(:game, black_player: @black_player)
end
let(:white_player) { create(:user) }
let(:black_player) { create(:user) }
let(:game) { create(:game, white_player: white_player, black_player: black_player) }

describe 'GET pieces#show' do
it 'responds successfully with an HTTP 200 status code' do
sign_in @black_player
@game.update_current_player!('white')
piece_id = @game.chess_pieces.find_by(position_x: 1, position_y: 7)
sign_in black_player
game.update_current_player!('white')
piece_id = game.chess_pieces.find_by(position_x: 1, position_y: 7)
get :show, id: piece_id

expect(response).to be_success
expect(response).to have_http_status(200)
end

it 'redirects to login if not signed in' do
piece_id = @game.chess_pieces.first.id
piece_id = game.chess_pieces.first.id
get :show, id: piece_id

expect(response).to redirect_to new_user_session_path
end
end

describe 'PUT pieces#update' do
let(:white_player) { create(:user) }
let(:game) { create(:game, white_player: white_player) }

it 'responds successfully with an HTTP 302 status code' do
sign_in white_player
piece_id = game.chess_pieces.find_by(position_x: 1, position_y: 2).id
Expand Down Expand Up @@ -65,8 +61,6 @@
end

describe 'GET pieces#valid_moves' do
let(:game) { create(:game) }

it 'returns valid moves for the piece' do
pawn = create(:game).chess_pieces.find_by(position_x: 1, position_y: 2)
sign_in pawn.game.white_player
Expand All @@ -86,4 +80,17 @@
expect(response).to have_http_status(:forbidden)
end
end

describe 'PUT pieces#promote' do
it 'promotes the pawn to the specified piece' do
sign_in white_player
pawn = create(:pawn, position_x: 1, position_y: 8, color: :white, game: game)
game.current_player_is_white_player!

put :promote, id: pawn, type: 'Queen'

expect(Queen.find(pawn.id)).to be_a Queen
expect(game.reload.current_player_is_black_player?).to eq true
end
end
end
24 changes: 24 additions & 0 deletions spec/models/pawn_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,28 @@
expect(Pawn.find_by(id: pawn.id)).to be_nil
end
end

describe '#promotable?' do
it 'is false when not in rank 1 or 8' do
pawn = build(:pawn, position_x: 1, position_y: 4)

expect(pawn.promotable?).to eq false
end

it 'is true when promotion is available' do
pawn = build(:pawn, position_x: 1, position_y: 8)

expect(pawn.promotable?).to eq true
end
end

describe '#promote_to!' do
it 'promotes the pawn to the specified type' do
pawn = build(:pawn, position_x: 1, position_y: 8)

pawn.promote_to!('Queen')

expect(pawn.type).to eq 'Queen'
end
end
end