Skip to content

Commit

Permalink
Flesh out the bones of a push-your-luck die game
Browse files Browse the repository at this point in the history
OK so I should have done many more commits but I was YOLOCODIN'

Players can now:

- Request a Survey License for a new Star System from Universal
- Launch a Survey, with up to 18 Sensors!
- Lock in Sensor Data
- Return home (or... just... keep... trying)

I need to figure out:

- How to add some *tension* beyond just maximizing points.
- What to do with the XP; I presume I'll want to level up the Survey
  License somehow
- A way to evaluate the Survey so there's a "reward"
- How do I want to integrate card-game mechanics? Maybe Surveyors have a
  hand of cards they can use to futz with the roll (bump die, for
  example)
- It could be neat to have "Ships" be how we determine how many
  Sensors Surveys bring and in what configuration.
  • Loading branch information
zspencer committed Nov 19, 2023
1 parent 79b4f02 commit 510d904
Show file tree
Hide file tree
Showing 41 changed files with 725 additions and 89 deletions.
14 changes: 12 additions & 2 deletions app/furniture/slipvector/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,15 @@ en:
surveyors_guild:
star_systems:
create:
success: "Surveyed Star System!"
failure: "Couldn't Survey Star System!"
success: "Registered Star System!"
failure: "Couldn't Register Star System!"
star_system:
surveys:
create:
success: "Launched a Survey in Star System"
failure: "Couldn't Survey Star System"
survey:
rolls:
create:
success: "Experiment Complete!"
failure: "Experiment Failed!"
5 changes: 5 additions & 0 deletions app/furniture/slipvector/record.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class Slipvector
class Record < ApplicationRecord
extend StripsNamespaceFromModelName
end
end
14 changes: 0 additions & 14 deletions app/furniture/slipvector/star_system.rb

This file was deleted.

9 changes: 0 additions & 9 deletions app/furniture/slipvector/star_system_policy.rb

This file was deleted.

This file was deleted.

12 changes: 0 additions & 12 deletions app/furniture/slipvector/survey.rb

This file was deleted.

8 changes: 6 additions & 2 deletions app/furniture/slipvector/surveyors_guild/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ class Slipvector
class SurveyorsGuild
class Routes
def self.append_routes(router)
router.resources(:surveyors_guilds, only: [:show], module: "slipvector/surveyors_guild") do
router.resources(:star_systems, only: [:create])
router.resources(:surveyors_guilds, only: [:show], module: "slipvector") do
router.resources(:star_systems, only: [:new, :create, :show], module: "surveyors_guild") do
router.resources(:surveys, only: [:create, :show, :update], module: "star_system") do
router.resources(:rolls, only: [:create], module: "survey")
end
end
end
end
end
Expand Down
24 changes: 24 additions & 0 deletions app/furniture/slipvector/surveyors_guild/star_system.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class Slipvector
class SurveyorsGuild
class StarSystem < Record
self.table_name = "slipvector_star_systems"

belongs_to :surveyors_guild, inverse_of: :star_systems
location(parent: :surveyors_guild)

has_many :surveys, inverse_of: :star_system, dependent: :destroy

def biological_data
surveys.complete.sum(:biological_data)
end

def material_data
surveys.complete.sum(:material_data)
end

def energy_data
surveys.complete.sum(:energy_data)
end
end
end
end
59 changes: 59 additions & 0 deletions app/furniture/slipvector/surveyors_guild/star_system/survey.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
class Slipvector
class SurveyorsGuild::StarSystem::Survey < Record
self.table_name = "slipvector_surveys"

belongs_to :star_system, inverse_of: :surveys
location(parent: :star_system)
has_one :surveyors_guild, through: :star_system

has_many :rolls, inverse_of: :survey

attribute :biological_data, :integer, default: 0
attribute :biological_dice_count, :integer, default: 0
validates :biological_dice_count, numericality: {only_integer: true, in: (0..6)}

attribute :material_data, :integer, default: 0
attribute :material_dice_count, :integer, default: 0
validates :material_dice_count, numericality: {only_integer: true, in: (0..6)}

attribute :energy_data, :integer, default: 0
attribute :energy_dice_count, :integer, default: 0
validates :energy_dice_count, numericality: {only_integer: true, in: (0..6)}

after_create :create_first_roll

before_validation :cache_results, if: -> { will_save_change_to_attribute?(:status, from: :in_progress, to: :complete) }

enum status: {
preparing: "preparing",
in_progress: "in_progress",
complete: "complete"
}

def create_first_roll
rolls.create_first!(seed: id.hash, biological_dice_count:, material_dice_count:, energy_dice_count:) if rolls.empty?
end

def cache_results
self.biological_data = last_roll.biological_data
self.energy_data = last_roll.energy_data
self.material_data = last_roll.material_data
self.efficiency = last_roll.efficiency
self.score = last_roll.total_score
end

def last_roll
@last_roll ||= rolls.by_turn.last
end

def next_roll
return create_first_roll if rolls.empty?

rolls.new(turn: last_roll.turn + 1,
preceding_roll: last_roll,
biological_dice: last_roll.biological_dice,
material_dice: last_roll.material_dice,
energy_dice: last_roll.energy_dice)
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
class Slipvector
class SurveyorsGuild::StarSystem::Survey::Roll < Record
self.table_name = "slipvector_survey_rolls"
belongs_to :survey
location(parent: :survey)
belongs_to :preceding_roll, inverse_of: :succeeding_roll, class_name: "Roll", optional: true
has_one :succeeding_roll, inverse_of: :preceding_roll, foreign_key: :preceding_roll, class_name: "Roll"

attribute :biological_dice, DiceType.new
delegate :score, :count, :kept, :kept=, to: :biological_dice, prefix: true
alias_method :biological_data, :biological_dice_score

attribute :material_dice, DiceType.new
delegate :score, :count, :kept, :kept=, to: :material_dice, prefix: true
alias_method :material_data, :material_dice_score

attribute :energy_dice, DiceType.new
delegate :score, :count, :kept, :kept=, to: :energy_dice, prefix: true
alias_method :energy_data, :energy_dice_score

scope :by_turn, -> { order(turn: :asc) }

def total_score
dice_score * bonus
end

def dice_score
(biological_data + material_data + energy_data)
end

def bonus
set_bonus * roll_bonus
end

def efficiency
total_score / (turn * all_dice.length)
end

def roll_bonus
case turn
when 1..2
4
when 3..4
2
else
1
end
end

def set_bonus
sets = all_dice.group_by(&:value)
case sets
when 1
3
when 2
2
else
1
end
end

def all_dice
biological_dice + material_dice + energy_dice
end

def self.next(attributes)
return create_first! if blank?

last_roll = by_turn.last
last_roll.biological_dice_kept = attributes[:biological_dice_kept]
last_roll.material_dice_kept = attributes[:material_dice_kept]
last_roll.energy_dice_kept = attributes[:energy_dice_kept]
seed = last_roll.id.hash
new(turn: last_roll.turn + 1, preceding_roll: last_roll,
biological_dice: Shaker.new(seed: seed + 1, dice: last_roll.biological_dice).shake,
material_dice: Shaker.new(seed: seed + 2, dice: last_roll.material_dice).shake,
energy_dice: Shaker.new(seed: seed + 2, dice: last_roll.energy_dice).shake)
end

def self.create_first!(seed:, biological_dice_count:, material_dice_count:, energy_dice_count:)
create!(turn: 1,
biological_dice: Shaker.new_from_count(seed: seed + 1, count: biological_dice_count).shake,
material_dice: Shaker.new_from_count(seed: seed + 2, count: material_dice_count).shake,
energy_dice: Shaker.new_from_count(seed: seed + 3, count: energy_dice_count).shake)
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
class Slipvector
class SurveyorsGuild::StarSystem::Survey::Roll
class Dice
include ActiveModel::Model
include ActiveModel::Attributes

attribute :kept, array: true, skidefault: -> { [] }
attribute :dice, DieType.new, array: true, default: -> { [] }

delegate :[], :length, :map, :each, :reduce, :group_by, to: :dice
alias_method :count, :length

def score
return 0 if length.zero?
base_score * set_bonus
end

def kept
dice.select(&:kept?).map(&:index)
end

def kept=(indexes)
return if indexes.nil?
dice.each do |dice|
indexes.include?(dice.index) ? dice.keep! : dice.leave!
end

kept
end

def base_score
@base_score ||= grouped_dice.reduce(0) do |result, (value, dice)|
result + (value * dice.length)
end
end

def set_bonus
@set_bonus ||= case grouped_dice.length
when 1
3
when 2
2
else
1
end
end

def grouped_dice
@grouped_dice ||= group_by(&:value)
end

def +(other)
Dice.new(dice: (dice + other.dice))
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class Slipvector
class SurveyorsGuild::StarSystem::Survey::Roll
class DiceType < ActiveRecord::Type::Json
def deserialize(string)
return Dice.new(dice: []) if string.nil?
Dice.new(super)
end

def serialize(dice)
if dice.is_a?(Array)
super(Dice.new(dice: dice))
elsif dice.is_a?(Dice)
super(dice)
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class Slipvector
class SurveyorsGuild::StarSystem::Survey::Roll
class Die
include ActiveModel::Model
include ActiveModel::Attributes

attribute :sides, :integer, default: 6
attribute :value, :integer, default: 1
attribute :index, :integer, default: nil
attribute :kept, :boolean, default: false
alias_method :kept?, :kept

def keep!
self.kept = true
end

def leave!
self.kept = false
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class Slipvector
class SurveyorsGuild::StarSystem::Survey::Roll
class DieType < ActiveRecord::Type::Json
def deserialize(string)
return nil if string.nil?
super.map do |die|
Die.new(**die)
end
end

def serialize(die)
super
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
class Slipvector
class SurveyorsGuild::StarSystem::Survey::Roll
class Shaker
include ActiveModel::Model
attr_accessor :rng, :dice
def initialize(seed:, dice: [])
self.rng = Random.new(seed)
super(dice: dice)
end

def self.new_from_count(seed:, count:)
new(dice: Dice.new(dice: (count.times.map { |index| Die.new(value: 0, index:) })), seed: seed)
end

def shake
dice.each do |die|
next if die.kept?
die.value = rng.rand(die.sides) + 1
end
end
end
end
end
Loading

0 comments on commit 510d904

Please sign in to comment.