Skip to content

Commit

Permalink
fix random seed with new seed_v2 db settings field
Browse files Browse the repository at this point in the history
* past games have a `seed` that is set when the game is started, that is only
  used for player order
* the new `seed_v2` is used for player order, as well as the in-game `@seed`
  used by `rand`
* new games don't have `seed`, but old games with `seed` and not `seed_v2` will
  continue to work as before, with `@seed` being set based on the game's ID

Fixes #7465
  • Loading branch information
michaeljb committed Dec 31, 2023
1 parent d63e11f commit 81671fe
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 18 deletions.
2 changes: 1 addition & 1 deletion assets/app/view/create_game.rb
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ def submit
optional_rules: game_params[:optional_rules] || [],
},
}
game_data[:settings][:seed] = game_params[:seed] if game_params[:seed]
game_data[:settings][:seed_v2] = game_params[:seed] if game_params[:seed]
end

checked_options = Engine.meta_by_title(game_data[:title])
Expand Down
40 changes: 25 additions & 15 deletions lib/engine/game/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,15 @@

module Engine
module Game
def self.load(data, at_action: nil, actions: nil, pin: nil, optional_rules: nil, user: nil, **kwargs)
def self.load(data, at_action: nil, actions: nil, pin: nil, seed: nil, optional_rules: nil, user: nil, **kwargs)
case data
when String
parsed_data = JSON.parse(File.exist?(data) ? File.read(data) : data)
return load(parsed_data,
data = JSON.parse(File.exist?(data) ? File.read(data) : data)
return load(data,
at_action: at_action,
actions: actions,
pin: pin,
pin: pin || data.dig('settings', 'pin'),
seed: seed || data.dig('settings', 'seed_v2'),
optional_rules: optional_rules,
user: user,
**kwargs)
Expand All @@ -51,13 +52,16 @@ def self.load(data, at_action: nil, actions: nil, pin: nil, optional_rules: nil,
id = data['id']
actions ||= data['actions'] || []
pin ||= data.dig('settings', 'pin')
seed ||= data.dig('settings', 'seed_v2')
optional_rules ||= data.dig('settings', 'optional_rules') || []
when Integer
return load(::Game[data],
db_game = ::Game[data]
return load(db_game,
at_action: at_action,
actions: actions,
pin: pin,
optional_rules: optional_rules,
pin: pin || db_game.settings['pin'],
seed: seed || db_game.settings['seed_v2'],
optional_rules: optional_rules || db_game.settings['optional_rules'],
user: user,
**kwargs)
when ::Game
Expand All @@ -66,11 +70,12 @@ def self.load(data, at_action: nil, actions: nil, pin: nil, optional_rules: nil,
id = data.id
actions ||= data.actions.map(&:to_h)
pin ||= data.settings['pin']
seed ||= data.settings['seed_v2']
optional_rules ||= data.settings['optional_rules'] || []
end

Engine.game_by_title(title).new(
names, id: id, actions: actions, at_action: at_action, pin: pin, optional_rules: optional_rules, user: user, **kwargs
names, id: id, actions: actions, at_action: at_action, pin: pin, seed: seed, optional_rules: optional_rules, user: user, **kwargs
)
end

Expand All @@ -83,7 +88,7 @@ class Base
:tiles, :turn, :total_loans, :undo_possible, :redo_possible, :round_history, :all_tiles,
:optional_rules, :exception, :last_processed_action, :broken_action,
:turn_start_action_id, :last_turn_start_action_id, :programmed_actions, :round_counter,
:manually_ended
:manually_ended, :seed

# Game end check is described as a dictionary
# with reason => after
Expand Down Expand Up @@ -498,7 +503,7 @@ def game_instance?
true
end

def initialize(names, id: 0, actions: [], at_action: nil, pin: nil, strict: false, optional_rules: [], user: nil)
def initialize(names, id: 0, actions: [], at_action: nil, pin: nil, strict: false, optional_rules: [], user: nil, seed: nil)
@id = id
@turn = 1
@final_turn = nil
Expand All @@ -525,7 +530,7 @@ def initialize(names, id: 0, actions: [], at_action: nil, pin: nil, strict: fals

@optional_rules = init_optional_rules(optional_rules)

@seed = @id.to_s.scan(/\d+/).first.to_i % RAND_M
initialize_seed(seed)

case self.class::DEV_STAGE
when :prealpha
Expand Down Expand Up @@ -607,12 +612,17 @@ def initialize(names, id: 0, actions: [], at_action: nil, pin: nil, strict: fals
@log << '----'
end

def initialize_seed(seed)
@seed = (seed || @id.to_s.scan(/\d+/).first.to_i) % RAND_M
@rand = @seed
end

def rand
@seed =
@rand =
if RUBY_ENGINE == 'opal'
`parseInt(Big(#{RAND_A}).times(#{@seed}).plus(#{RAND_C}).mod(#{RAND_M}).toString())`
`parseInt(Big(#{RAND_A}).times(#{@rand}).plus(#{RAND_C}).mod(#{RAND_M}).toString())`
else
((RAND_A * @seed) + RAND_C) % RAND_M
((RAND_A * @rand) + RAND_C) % RAND_M
end
end

Expand Down Expand Up @@ -918,7 +928,7 @@ def next_turn!
end

def clone(actions)
self.class.new(@names, id: @id, pin: @pin, actions: actions, optional_rules: @optional_rules)
self.class.new(@names, id: @id, pin: @pin, seed: @seed, actions: actions, optional_rules: @optional_rules)
end

def trains
Expand Down
2 changes: 1 addition & 1 deletion models/game.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def ordered_players

players
.sort_by(&:id)
.shuffle(random: Random.new(settings['seed'] || 1))
.shuffle(random: Random.new(settings['seed'] || settings['seed_v2'] || 1))
end

def finished_at_ts
Expand Down
2 changes: 1 addition & 1 deletion routes/game.rb
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ class Api
min_players: r['min_players'],
max_players: r['max_players'],
settings: {
seed: (r['seed'] || Random.new_seed) % (2**31),
seed_v2: (r['seed'] || Random.new_seed) % (2**31),
player_order: r['player_order'],
unlisted: r['unlisted'],
optional_rules: r['optional_rules'],
Expand Down
2 changes: 2 additions & 0 deletions validate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ def run_game(game, actions = nil, strict: false)
engine = Engine::Game.load(game, strict: strict)
rescue Exception => e # rubocop:disable Lint/RescueException
$count += 1
time = Time.now - time
$total_time += time
data['finished']=false
#data['stack']=e.backtrace
data['exception']=e
Expand Down

0 comments on commit 81671fe

Please sign in to comment.