Add seat management fix new game button

This commit is contained in:
Jesse C. Fisher 2021-11-17 07:56:39 -08:00
parent 3a09b9bcff
commit 3d46e5b806
16 changed files with 161 additions and 46 deletions

View File

@ -1,5 +1,7 @@
class SeatsController < ApplicationController class SeatsController < ApplicationController
before_action :set_seat, only: [:show, :edit, :update, :destroy, :sit, :stand, :add_bot, :remove_bot] before_action :set_seat, only: [:show, :edit, :update, :destroy,
:sit, :stand, :add_bot, :remove_bot, :empty,
:remove_player]
respond_to :html, :json respond_to :html, :json
@ -90,6 +92,16 @@ class SeatsController < ApplicationController
redirect_to @seat.table, notice: @message redirect_to @seat.table, notice: @message
end end
def empty
@message = @seat.empty
redirect_to @seat.table, notice: @message
end
def remove_player
@message = @seat.remove_player
redirect_to @seat.table, notice: @message
end
private private
# Use callbacks to share common setup or constraints between actions. # Use callbacks to share common setup or constraints between actions.
def set_seat def set_seat

View File

@ -30,9 +30,16 @@ class TablesController < ApplicationController
# GET /tables/1 # GET /tables/1
# GET /tables/1.json # GET /tables/1.json
def show def show
# TODO move this to the game.rb model
# Clear play to beat if play returns to owner
# TODO refactor to ensure play for 2nd and 3rd place # TODO refactor to ensure play for 2nd and 3rd place
if @game.controlling_player.try("is_bot?")
flash[:notice] = "Bot is passes"
@game.controlling_player.pass
@game.save
@game.reload
end
# Clear play to beat if play returns to owner
if @game.play_to_beat.try(:player).try(:id) == @game.controlling_player_id if @game.play_to_beat.try(:player).try(:id) == @game.controlling_player_id
@game.play_to_beat_id = nil @game.play_to_beat_id = nil
@game.save @game.save
@ -41,7 +48,11 @@ class TablesController < ApplicationController
def new_game def new_game
@game = @table.add_game @game = @table.add_game
@game.start begin
@game.start
rescue
flash[:errors] = @game.errors
end
redirect_to @table redirect_to @table
end end

View File

@ -7,6 +7,7 @@ class Game < ActiveRecord::Base
has_many :player_cards, through: :players has_many :player_cards, through: :players
has_many :plays, through: :players has_many :plays, through: :players
belongs_to :play_to_beat belongs_to :play_to_beat
belongs_to :controlling_player, class_name: "Player", foreign_key: "controlling_player_id"
accepts_nested_attributes_for :players accepts_nested_attributes_for :players
#validates :title, presence: true #validates :title, presence: true
after_create :add_table after_create :add_table
@ -79,6 +80,12 @@ class Game < ActiveRecord::Base
new_player = self.players.create(user: user, soft_token: user.soft_token) new_player = self.players.create(user: user, soft_token: user.soft_token)
end end
def add_player_bot
# todo return if game is full
# return false unless self.joinable?(user)
new_player = self.players.create(is_bot: true)
end
def remove_player_from_user(user) def remove_player_from_user(user)
return false unless self.has_user? user return false unless self.has_user? user
@ -116,7 +123,7 @@ class Game < ActiveRecord::Base
def full? def full?
players.count >= 4 # players.count >= 4
end end
def set_controlling_player(arg) def set_controlling_player(arg)
@ -132,7 +139,8 @@ class Game < ActiveRecord::Base
end end
def joinable? user def joinable? user
!self.has_user?(user) && !self.full? # !self.has_user?(user) && !self.full?
!self.full?
end end
def player_ids_by_seat_order def player_ids_by_seat_order
@ -146,13 +154,6 @@ class Game < ActiveRecord::Base
player_ids_by_seat_order[(@controlling_player_index + 1) % self.player_ids_by_seat_order.length] player_ids_by_seat_order[(@controlling_player_index + 1) % self.player_ids_by_seat_order.length]
end end
# The player whose turn it is
def controlling_player
if self.controlling_player_id
self.players.find_by_id(self.controlling_player_id)
end
end
# The user whose turn it is # The user whose turn it is
def controlling_user def controlling_user
self.players.find(self.controlling_player_id).user self.players.find(self.controlling_player_id).user
@ -182,8 +183,9 @@ private
self.table ||= Table.create current_game_id: self.id self.table ||= Table.create current_game_id: self.id
self.seats = self.table.seats self.seats = self.table.seats
self.seats.each do |seat| self.seats.each do |seat|
if (seat.user_id || seat.user_soft_token) # if (seat.user_id || seat.user_soft_token)
new_player = self.players.create(user_id: seat.user_id, soft_token: seat.user_soft_token) if (seat.occupied?)
new_player = self.players.create(user_id: seat.user_id, soft_token: seat.user_soft_token, is_bot: seat.player.is_bot)
seat.player = new_player seat.player = new_player
seat.save seat.save
end end

View File

@ -51,10 +51,15 @@ class Seat < ActiveRecord::Base
if self.player == nil if self.player == nil
# add player to game # add player to game
@bot_player = self.table.current_game.players.new(is_bot: true) @bot_player = self.table.current_game.players.new(is_bot: true)
# @bot_player = self.table.current_game.add_player_from_user(current_user).id
self.player = @bot_player self.player = @bot_player
self.player.save self.player.save
self.save self.save
return "Added Bot" return "Added Bot"
else
self.player.is_bot = true
self.player.save
return "Added Bot"
end end
return "Failed to add Bot" return "Failed to add Bot"
@ -75,6 +80,31 @@ class Seat < ActiveRecord::Base
end end
end end
def empty
self.player.user = nil
self.player.soft_token = nil
self.player.is_bot = false
self.player.save
self.user_id = nil
self.user_soft_token = nil
# self.player_id = nil
self.save
return "Successfully stood up."
end
def remove_player
if !self.player
return "No player to remove"
else
self.player.destroy
self.user_id = nil
self.user_soft_token = nil
# self.player_id = nil
self.save
return "Successfully removed player"
end
end
def occupied? def occupied?
self.user_id.present? || self.user_soft_token.present? || self.try("player").try("is_bot?") == true self.user_id.present? || self.user_soft_token.present? || self.try("player").try("is_bot?") == true
end end

View File

@ -3,18 +3,23 @@
.seat-controls .seat-controls
- if seat.occupied? - if seat.occupied?
- if seat.player.is_bot? - if seat.player.is_bot?
= button_to remove_bot_seat_path(seat) do -# = button_to remove_bot_seat_path(seat) do
- "- Bot" - "- Bot"
- if seat.occupied_by? current_user - if seat.occupied_by? current_user
= button_to stand_seat_path(seat), :data => { :confirm => 'Are you sure you want to leave this seat?' } do = button_to stand_seat_path(seat), :data => { :confirm => 'Are you sure you want to leave this seat?' } do
- "Stand" - "Leave"
- else
= button_to empty_seat_path(seat), :data => { :confirm => 'Are you sure you want to empty this seat?' } do
- "Empty"
- else - else
= button_to sit_seat_path(seat) do = button_to sit_seat_path(seat) do
- "Sit" - "Sit"
= button_to add_bot_seat_path(seat) do = button_to add_bot_seat_path(seat) do
- "+ Bot" - "+ Bot"
- if seat.player - if seat.player
= button_to remove_player_seat_path(seat), :data => { :confirm => 'Are you sure you want to remove this player?' } do
- "Remove Player"
.player{id: "player-#{seat.player.id}", class: @game.controlling_player == seat.player ? "taking-turn" : "", .player{id: "player-#{seat.player.id}", class: @game.controlling_player == seat.player ? "taking-turn" : "",
data: {soft_token: seat.player.soft_token} } data: {soft_token: seat.player.soft_token} }
.user-name= seat.player.display_name .user-name= seat.player.display_name

View File

@ -15,6 +15,9 @@
- if @game.winner_player_id || @game.nil? - if @game.winner_player_id || @game.nil?
= button_to 'New Game', new_game_table_path(@table) = button_to 'New Game', new_game_table_path(@table)
-# - if @game && !@game.startable?
= button_to 'New Game', new_game_table_path(@table)
#game-component #game-component
- if @game.winner_player_id - if @game.winner_player_id
%h1= @game.status %h1= @game.status

View File

@ -54,4 +54,5 @@ Rails.application.configure do
# Add Rack::LiveReload to the bottom of the middleware stack with the default options. # Add Rack::LiveReload to the bottom of the middleware stack with the default options.
config.middleware.insert_after ActionDispatch::Static, Rack::LiveReload config.middleware.insert_after ActionDispatch::Static, Rack::LiveReload
BetterErrors::Middleware.allow_ip! '10.0.0.0/16'
end end

View File

@ -3,6 +3,7 @@ Rails.application.routes.draw do
resources :tables do resources :tables do
member do member do
post 'new_game' post 'new_game'
post 'end_game'
end end
resources :seats do resources :seats do
member do member do
@ -23,6 +24,8 @@ Rails.application.routes.draw do
post 'stand' post 'stand'
post 'add_bot' post 'add_bot'
post 'remove_bot' post 'remove_bot'
post 'empty'
post 'remove_player'
end end
end end

View File

@ -14,6 +14,7 @@ feature 'Add bot', type: :feature do
scenario 'visitor can add a bot if the seat is empty' do scenario 'visitor can add a bot if the seat is empty' do
@button = page.find_button('+ Bot', match: :first).click @button = page.find_button('+ Bot', match: :first).click
expect(page).to have_content "BotPlayer" expect(page).to have_content "BotPlayer"
end end
scenario 'visitor can not add a bot if the seat is occupied by bot' do scenario 'visitor can not add a bot if the seat is occupied by bot' do

View File

@ -0,0 +1,35 @@
# Feature: Bot takes turns
# As a player
# I want bots to take turns
# So I can play against the computer
feature 'Bot takes turn', type: :feature do
before(:each) do
@table = FactoryGirl.create :table
@game = @table.current_game
visit table_path @table
end
scenario 'bot player passes' do
@button = page.find_button('+ Bot', match: :first).click
expect(page).to have_content "BotPlayer"
@button = page.find_button('Sit', match: :first).click
click_button "Start"
click_button 'Pass'
expect(page).to have_content "Bot is passes"
end
scenario 'bot player plays single' do
@button = page.find_button('+ Bot', match: :first).click
expect(page).to have_content "BotPlayer"
@button = page.find_button('Sit', match: :first).click
click_button "Start"
@game = @table.current_game
if @game.controlling_player.soft_token == page.current_user.soft_token
@card_to_play = @game.controlling_player.inventory.first
find("[data-card-name='#{@card_to_play.to_s}']").click
click_button 'Play Hand'
end
expect(@game.play_to_beat.player.is_bot?).to eq true
expect(@game.play_to_beat.hand_type).to eq "single"
end
end

View File

@ -2,7 +2,7 @@ require 'rails_helper'
RSpec.feature "EndOfGames", type: :feature do RSpec.feature "EndOfGames", type: :feature do
# Given I am in a started game # Given I am in a started game
before(:each) do before(:all) do
# If test is unable to click correct card, use wider window # If test is unable to click correct card, use wider window
# page.driver.browser.manage.window.resize_to(1640, 1090) # page.driver.browser.manage.window.resize_to(1640, 1090)
@game = setup_game @game = setup_game
@ -10,11 +10,7 @@ RSpec.feature "EndOfGames", type: :feature do
@player = @game.controlling_player @player = @game.controlling_player
signin(@player.user.email,'password') signin(@player.user.email,'password')
visit table_path @game.table visit table_path @game.table
end
# And it is my turn
# And I play my last cards
# Then I win the game
scenario 'I win the game' do
# Play lowest card # Play lowest card
# Other players always pass # Other players always pass
# Play next lowest card # Play next lowest card
@ -41,11 +37,23 @@ RSpec.feature "EndOfGames", type: :feature do
@game.reload @game.reload
end end
end end
end
# And it is my turn
# And I play my last cards
# Then I win the game
scenario 'I win the game' do
# After I play my last card(s) # After I play my last card(s)
# Expect that I am the winner # Expect that I am the winner
@game.reload @game.reload
expect(@game.winner).to eq @player expect(@game.winner).to eq @player
click_button "New Game"
expect(page).to have_content "Game started"
end
scenario 'I start a new game at the same table' do
click_button "New Game"
expect(page).to have_content "Game started"
end end
describe 'I lose the game' do describe 'I lose the game' do

View File

@ -168,11 +168,12 @@ feature 'Play a hand', type: :feature do
# Given I am the active player # Given I am the active player
# When I pass # When I pass
# Then I see an successful pass message # Then I see an successful pass message
scenario 'active player can pass', js: true do # scenario 'active player can pass', js: true do
scenario 'active player can pass' do
@current_user = @game.controlling_player @current_user = @game.controlling_player
accept_confirm do # accept_confirm do
click_button 'Pass' click_button 'Pass'
end # end
# click_button "pass_hand_button_#{@player.id}" # click_button "pass_hand_button_#{@player.id}"
## TODO: Flash messages ## TODO: Flash messages
expect(page).to have_content 'Successfully passed.' expect(page).to have_content 'Successfully passed.'

View File

@ -28,7 +28,7 @@ feature 'Seats', :devise do
@button = page.find_button('Sit', match: :first) @button = page.find_button('Sit', match: :first)
@button.click @button.click
expect(page).to have_content "Guest" expect(page).to have_content "Guest"
@buttons = page.find_all('button', text: 'Stand') @buttons = page.find_all('button', text: 'Leave')
expect(@buttons.length).to eq 1 expect(@buttons.length).to eq 1
end end
@ -62,13 +62,14 @@ feature 'Seats', :devise do
find_all(:button, "Sit") find_all(:button, "Sit")
end end
scenario 'visitor can leave the current game', js: true do # scenario 'visitor can leave the current game', js: true do
scenario 'seated visitor can leave the current game' do
click_button 'Sit', match: :first click_button 'Sit', match: :first
expect(page).to have_content 'Guest' expect(page).to have_content 'Guest'
expect(page.text.scan('Guest').count).to eq 1 expect(page.text.scan('Guest').count).to eq 1
accept_confirm do # accept_confirm do
click_button 'Stand', match: :first click_button 'Leave', match: :first
end # end
expect(page.find('.seat.position-1')).to have_content 'Sit' expect(page.find('.seat.position-1')).to have_content 'Sit'
expect(sit_buttons.length).to eq 4 expect(sit_buttons.length).to eq 4
expect(page.text.scan('Guest').count).to eq 0 expect(page.text.scan('Guest').count).to eq 0
@ -95,13 +96,14 @@ feature 'Seats', :devise do
expect(page.text.scan(@user.email).count).to eq 1 expect(page.text.scan(@user.email).count).to eq 1
end end
scenario 'user can leave the current game', js: true do # scenario 'user can leave the current game', js: true do
scenario 'user can leave the current game' do
click_button 'Sit', match: :first click_button 'Sit', match: :first
expect(page).to have_content @user.email expect(page).to have_content @user.email
expect(page.text.scan(@user.email).count).to eq 1 expect(page.text.scan(@user.email).count).to eq 1
accept_confirm do # accept_confirm do
click_button 'Stand', match: :first click_button 'Leave', match: :first
end # end
expect(page.text.scan(@user.email).count).to eq 0 expect(page.text.scan(@user.email).count).to eq 0
end end

View File

@ -21,13 +21,14 @@ feature 'Table show', :devise do
expect(page.text.scan('Guest').count).to eq 1 expect(page.text.scan('Guest').count).to eq 1
end end
scenario 'visitor can leave the current game', js: true do # scenario 'visitor can leave the current game', js: true do
scenario 'visitor can leave the current game' do
click_button 'Sit', match: :first click_button 'Sit', match: :first
expect(page).to have_content "Guest" expect(page).to have_content "Guest"
expect(page.text.scan('Guest').count).to eq 1 expect(page.text.scan('Guest').count).to eq 1
accept_confirm do # accept_confirm do
click_button 'Stand', match: :first click_button 'Stand', match: :first
end # end
expect(page.find('.seat.position-1')).to have_content "Sit" expect(page.find('.seat.position-1')).to have_content "Sit"
expect(page.text.scan('Guest').count).to eq 0 expect(page.text.scan('Guest').count).to eq 0
end end
@ -56,14 +57,15 @@ feature 'Table show', :devise do
expect(page.text.scan(@user.email).count).to eq 1 expect(page.text.scan(@user.email).count).to eq 1
end end
scenario 'user can leave the current game', js: true do # scenario 'user can leave the current game', js: true do
scenario 'user can leave the current game' do
expect(page).to have_content "Sit" expect(page).to have_content "Sit"
click_button 'Sit', match: :first click_button 'Sit', match: :first
expect(page).to have_content @user.email expect(page).to have_content @user.email
expect(page.text.scan(@user.email).count).to eq 1 expect(page.text.scan(@user.email).count).to eq 1
accept_confirm do # accept_confirm do
click_button 'Stand', match: :first click_button 'Stand', match: :first
end # end
expect(page.find('.seat.position-1')).to have_content "Sit" expect(page.find('.seat.position-1')).to have_content "Sit"
expect(page.text.scan(@user.email).count).to eq 0 expect(page.text.scan(@user.email).count).to eq 0
end end

View File

@ -17,7 +17,8 @@ feature 'Navigation links', :devise do
# Given I am a visitor # Given I am a visitor
# When I click a navigation link # When I click a navigation link
# Then I see the proper web page # Then I see the proper web page
scenario 'follow navigation links', js: :true do # scenario 'follow navigation links', js: :true do
scenario 'follow navigation links' do
visit root_path visit root_path
@links = { @links = {
'Play Now': '"/tables/1"', 'Play Now': '"/tables/1"',
@ -26,9 +27,6 @@ feature 'Navigation links', :devise do
} }
@links.each do |link| @links.each do |link|
click_link link[0] click_link link[0]
#TODO: Remove race condition
sleep 4
page.save_screenshot
expect(page.current_path).to eq(eval(link[1])) expect(page.current_path).to eq(eval(link[1]))
visit root_path visit root_path
end end

View File

@ -4,6 +4,7 @@ require 'spec_helper'
require File.expand_path('../../config/environment', __FILE__) require File.expand_path('../../config/environment', __FILE__)
require 'rspec/rails' require 'rspec/rails'
# Add additional requires below this line. Rails is not loaded until this point! # Add additional requires below this line. Rails is not loaded until this point!
`trash tmp/capybara/*`
require 'capybara-screenshot/rspec' require 'capybara-screenshot/rspec'
# Requires supporting ruby files with custom matchers and macros, etc, in # Requires supporting ruby files with custom matchers and macros, etc, in