Skip to content

Commit

Permalink
Update Bureaucrat to journaling
Browse files Browse the repository at this point in the history
All tests green; but attack processing (esp. Moat) only updated as far as
necessary.
Journals can now be hidden. This isn't used, but will be later for
automoating.
For #128
  • Loading branch information
asilano committed Aug 11, 2016
1 parent 2cd15e5 commit 9085a4c
Show file tree
Hide file tree
Showing 14 changed files with 148 additions and 126 deletions.
125 changes: 59 additions & 66 deletions app/models/base_game/bureaucrat.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,97 +6,90 @@ class BaseGame::Bureaucrat < Card
"his or her hand and puts it on top of their " +
"deck, or reveals a hand with no Victory cards."

def play(parent_act)
PlaceEventTempl = Journal::Template.new("{{player}} put {{card}} on top of their deck.")

def play
super

# First, acquire a Silver to top of deck.
silver_pile = game.piles.find_by_card_type("BasicCards::Silver")
player.gain(parent_act, :pile => silver_pile, :location => "deck")
silver_pile = game.piles.detect { |pile| pile.card_type == "BasicCards::Silver" }
player.gain(pile: silver_pile, location: "deck", journal: game.current_journal)

game.histories.create!(:event => "#{player.name} gained a Silver to top of their deck.",
:css_class => "player#{player.seat} card_gain")
game.add_history(:event => "#{player.name} gained a Silver to top of their deck.",
:css_class => "player#{player.seat} card_gain")

# Now, attack
attack(parent_act)
attack
end

def determine_controls(player, controls, substep, params)
determine_react_controls(player, controls, substep, params)
def determine_controls(actor, controls, question)
#determine_react_controls(player, controls, substep, params)

case substep
when "victory"
case question.method
when :resolve_victory
# Ask the attack target for a Victory card, or to reveal a hand devoid of
# all such.
controls[:hand] += [{:type => :button,
:action => :resolve,
:text => "Place",
:nil_action => nil,
:params => {:card => "#{self.class}#{id}",
:substep => "victory"},
:cards => player.cards.hand.map {|c| c.is_victory? }
controls[:hand] += [{type: :button,
text: "Place",
nil_action: nil,
journals: actor.cards.hand.each_with_index.map do |c, ix|
PlaceEventTempl.fill(player: actor.name, card: "#{c.readable_name} (#{ix})") if c.is_victory?
end
}]
end
end

def attackeffect(params)
# Effect of the attack succeeding - that is, ask the target to put a Victory
# card on top of their deck.
target = Player.find(params[:target])
# source = Player.find(params[:source])
parent_act = params[:parent_act]
target = params[:target]

target_journal_templ = Journal::Template.new(PlaceEventTempl.fill(player: target.name))
journal = game.find_journal(target_journal_templ)

if journal.nil?
target_victories = target.cards.hand.select { |c| c.is_victory? }

# Handle autocratting
target_victories = target.cards(true).hand.select {|c| c.is_victory?}
if (target.settings.autocrat_victory &&
target_victories.map(&:class).uniq.length == 1)
# Target is autocratting victories, and holding exactly one type of
# victory card. Find the index of that card, and pre-create the journal
vic = target_victories[0]
index = target.cards.hand.index(vic)

if (target.settings.autocrat_victory &&
target_victories.map {|c| c.class}.uniq.length == 1)
# Target is autocratting victories, and holding exactly one type of
# victory card. Find the index of that card, and call resolve_victory
vic = target_victories[0]
index = target.cards.hand.index(vic)
return resolve_victory(target, {:card_index => index}, parent_act)
elsif target_victories.empty?
# Target is holding no victories. Call resolve_victory for the nil_action
return resolve_victory(target, {:nil_action => true}, parent_act)
else
# Autocrat doesn't apply. Create the pending action to request the Victory
# card
parent_act.children.create!(:expected_action => "resolve_#{self.class}#{id}_victory",
:text => "Place a Victory card onto deck",
:player => target,
:game => game)
journal = game.add_journal(player_id: target.id,
event: target_journal_templ.fill(card: "#{vic.readable_name} (#{index})"))
elsif target_victories.empty?
# Target is holding no victories. Just log the "revealing" of the hand
game.add_history(:event => "#{target.name} revealed their hand to the Bureaucrat:",
:css_class => "player#{target.seat} card_reveal")
game.add_history(:event => "#{target.name} revealed #{target.cards.hand.map(&:readable_name).join(', ')}.",
:css_class => "player#{target.seat} card_reveal")
else
# Autocrat doesn't apply.
# Ask the required question, and escape this processing stack
game.ask_question(object: self, actor: target, method: :resolve_victory, text: "Place a Victory card onto deck.")
game.abort_journal
end
end

return "OK"
if journal
resolve_victory(journal, target)
end
end

# This is at the attack target either putting a card back on their deck,
# or revealing a hand devoid of victory cards.
resolves(:victory).validating_params_has_any_of(:nil_action, :card_index).
validating_param_is_card(:card_index, scope: :hand, &:is_victory?).
validating_param_present_only_if(:nil_action, description: 'you have no victory cards in hand') do
!actor.cards.hand.any?(&:is_victory?)
end.with do
# All looks good - process the request
if params.include? :nil_action
# :nil_action specified. "Reveal" the player's hand. Since no-one needs to
# act on the revealed cards, just add a history entry detailing them.
game.histories.create!(:event => "#{actor.name} revealed their hand to the Bureaucrat:",
:css_class => "player#{actor.seat} card_reveal")
game.histories.create!(:event => "#{actor.name} revealed #{actor.cards.hand.map {|c| c.class.readable_name}.join(', ')}.",
:css_class => "player#{actor.seat} card_reveal")
else
# :card_index specified. Place the specified card on top of the player's
# deck, and "reveal" it by creating a history.
card = actor.cards.hand[params[:card_index].to_i]
card.location = "deck"
card.position = -1
card.save!
actor.renum(:deck)
game.histories.create!(:event => "#{actor.name} put a #{card.class.readable_name} on top of their deck.",
:css_class => "player#{actor.seat}")
end

"OK"
resolves(:victory).using(PlaceEventTempl).
validating_param_is_card(:card, scope: :hand, &:is_victory?).
with do
# Place the specified card on top of the player's deck, and "reveal" it by creating a history.
Rails.logger.info("Bcat place")
card = journal.card
card.location = "deck"
card.position = -1
actor.renum(:deck)
game.add_history(:event => "#{actor.name} put a #{card.class.readable_name} on top of their deck.",
:css_class => "player#{actor.seat}")
end
end
4 changes: 2 additions & 2 deletions app/models/base_game/mine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def play
end

if journal
resolve_trash(player, journal)
resolve_trash(journal, player)
end
end

Expand Down Expand Up @@ -78,7 +78,7 @@ def determine_controls(actor, controls, question)
})

take_journal.params = {trashed_cost: trashed_cost}
resolve_take(actor, take_journal)
resolve_take(take_journal, actor)
end

resolves(:take).using(TakeEventTempl).
Expand Down
7 changes: 4 additions & 3 deletions app/models/game.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ module TurnPhases
attr_accessor *(Card.expansions.map {|set| "num_#{set.name.underscore}_cards".to_sym})
attr_accessor *(Card.expansions.map {|set| "#{set.name.underscore}_present".to_sym})
attr_accessor(*(1..10).map{|n| "pile_#{n}".to_sym})
attr_reader :current_journal

validates :name, :presence => true
validates :max_players, :presence => true, :numericality => true, :inclusion => { :in => 2..6, :message => 'must be between 2 and 6 inclusive' }
Expand Down Expand Up @@ -542,12 +543,12 @@ def hack_game_state(journal, check: false)
end

case journal.event
when /^Hack: (.*) (hand|deck|play|discard) (\+|=) *((?:[a-zA-Z]*::[a-zA-Z]*(?:,\ )?)*)$/
when /^Hack: (.*) (hand|deck|play|discard|enduring) (\+|=) *((?:[a-zA-Z]*::[a-zA-Z]*(?:,\ )?)*)$/
player = players.joins { user }.where { user.name == $1 }.first
location = $2

if $3 == '='
# Setting hand completely. So throw away existing cards
# Setting location completely. So throw away existing cards
cards.delete_if { |card| card.player == player && card.location == location }
end

Expand All @@ -563,7 +564,7 @@ def hack_game_state(journal, check: false)
self.cards << card_class.new(game: self,
player: player,
location: location,
position: player.cards.hand.length)
position: player.cards.in_location(location).length)
end
when /^Hack: ([a-zA-Z]*::[a-zA-Z]*) in (.*) remove (\d*|all)$/
card_type = $1
Expand Down
2 changes: 1 addition & 1 deletion app/models/journal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def fill(fields)
field = field.join(', ')
end

field
field || "{{#{$1}}}"
end
end

Expand Down
2 changes: 1 addition & 1 deletion app/models/pile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def cost
end

def empty?
cards(true).count == 0
cards.count == 0
end

def card_class
Expand Down
1 change: 0 additions & 1 deletion app/models/player.rb
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,6 @@ def start_turn

# Called by the game when it has nothing left to ask about, to see if the player needs to act or buy
def prompt_for_questions
Rails.logger.info("Prompting with #{num_actions} actions")
if num_actions > 0
# Ask the question - play action
game.turn_phase = Game::TurnPhases::ACTION
Expand Down
41 changes: 21 additions & 20 deletions app/views/games/_journals.html.slim
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
ul
- @game.journals.each do |journal|
li.journal class=classes_for_journal(journal, @player)
- if journal.modified
span.bold>[] (Edited)
- if journal.player == @player && journal.order > @game.last_blocked_journal
= best_in_place journal, :event, class: 'change_journal', other_fields: {game_id: @game.id}, html_attrs: {style: 'width: 100%', autocomplete: 'off'}
- else
span.immutable= journal.event
-if journal.errors.any?
ul.journal_errors.error
- journal.errors.full_messages.each do |msg|
li= msg
- if journal.histories.any?
ul.journal_histories
- journal.histories.each do |history|
- classes = history.css_class
- if @player && history.css_class =~ /\bplayer#{@player.seat}\b/
- classes += ' self'
li class=classes = format_history(history, @player)
- if journal.order >= @game.last_blocked_journal
span.insert data-order=journal.order+1
-if !journal.hidden || journal.modified
li.journal class=classes_for_journal(journal, @player)
- if journal.modified
span.bold>[] (Edited)
- if journal.player == @player && journal.order > @game.last_blocked_journal
= best_in_place journal, :event, class: 'change_journal', other_fields: {game_id: @game.id}, html_attrs: {style: 'width: 100%', autocomplete: 'off'}
- else
span.immutable= journal.event
-if journal.errors.any?
ul.journal_errors.error
- journal.errors.full_messages.each do |msg|
li= msg
- if journal.histories.any?
ul.journal_histories
- journal.histories.each do |history|
- classes = history.css_class
- if @player && history.css_class =~ /\bplayer#{@player.seat}\b/
- classes += ' self'
li class=classes = format_history(history, @player)
- if journal.order >= @game.last_blocked_journal
span.insert data-order=journal.order+1
5 changes: 5 additions & 0 deletions db/migrate/20160809114619_add_hidden_to_journals.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddHiddenToJournals < ActiveRecord::Migration
def change
add_column :journals, :hidden, :boolean
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20160312145617) do
ActiveRecord::Schema.define(version: 20160809114619) do

create_table "chats", force: true do |t|
t.integer "game_id"
Expand All @@ -38,6 +38,7 @@
t.datetime "created_at"
t.integer "order"
t.boolean "modified", default: false
t.boolean "hidden"
end

add_index "journals", ["game_id"], name: "index_journals_on_game_id"
Expand Down
28 changes: 11 additions & 17 deletions features/cards/base_game/bureaucrat.feature
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
Feature: Bureaucrat
Attack - Gain a Silver card; put it on top of your deck.
Each other player reveals a Victory card from his or her hand and puts it on top of their deck, or reveals a hand with no Victory cards.

Background:
Given I am a player in a standard game with Bureaucrat

Scenario: Bureaucrat should be set up at game start
Then there should be 10 Bureaucrat cards in piles
And there should be 0 Bureaucrat cards not in piles
Expand All @@ -20,15 +20,13 @@ Feature: Bureaucrat
And Charlie's deck is empty
And it is my Play Action phase
When I play Bureaucrat
And the game checks actions
Then the following 3 steps should happen at once
Then I should have put Silver on top of my deck
And Bob should need to place a Victory card onto deck
And Charlie should not need to act
When Bob chooses Estate in his hand
Then Bob should have put Estate from his hand on top of his deck
And it should be my Play Treasure phase
Then Bob should have put Estate from his hand on top of his deck
And it should be my Buy phase

Scenario: Playing Bureaucrat - Autocrat on
Given my hand contains Bureaucrat
And Bob's hand contains Great Hall, Great Hall, Copper
Expand All @@ -40,16 +38,15 @@ Feature: Bureaucrat
And Charlie's deck is empty
And it is my Play Action phase
When I play Bureaucrat
And the game checks actions
Then the following 3 steps should happen at once
Then the following 3 steps should happen at once
Then I should have put Silver on top of my deck
And Bob should have put Great Hall from his hand on top of his deck
And Bob should not need to act
And Charlie should need to place a Victory card onto deck
And Charlie should need to place a Victory card onto deck
When Charlie chooses Duchy in his hand
Then Charlie should have put Duchy from his hand on top of his deck
And it should be my Play Treasure phase
Then Charlie should have put Duchy from his hand on top of his deck
And it should be my Buy phase

Scenario: Playing Bureaucrat - Prevented by (Moat/Lighthouse)
Given my hand contains Bureaucrat
And Bob's hand contains Great Hall, Great Hall, Moat
Expand All @@ -63,11 +60,8 @@ Feature: Bureaucrat
And Charlie's deck is empty
And it is my Play Action phase
When I play Bureaucrat
And the game checks actions
Then the following 3 steps should happen at once
Then the following 3 steps should happen at once
Then I should have put Silver on top of my deck
And Bob should not need to act
And Charlie should not need to act

# Buy, not Play Treasure, because nothing blocked the advancement when we checked actions
And it should be my Buy phase
Loading

0 comments on commit 9085a4c

Please sign in to comment.