Refactoring: buildings
This commit is contained in:
parent
28c51ba518
commit
94caa93d7c
21
spec/building_spec.cr
Normal file
21
spec/building_spec.cr
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
require "./spec_helper"
|
||||||
|
|
||||||
|
describe Game::Building do
|
||||||
|
it "should create storehouse" do
|
||||||
|
bg = Game::Building.new Game::Building::Type::Storehouse, "Storehouse", storage: 100
|
||||||
|
bg.storage.should eq 100
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should create crystal miner" do
|
||||||
|
bg = Game::Building.new Game::Building::Type::CrystalMiner, "Cryslal Miner", **{
|
||||||
|
production: Game::Production.new(
|
||||||
|
ts: 20,
|
||||||
|
input: Game::Resources.new,
|
||||||
|
output: Game::Resources.new({
|
||||||
|
Game::Resources::Type::Crystals => 100,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
}
|
||||||
|
bg.production.as(Game::Production).ts.should eq 20
|
||||||
|
end
|
||||||
|
end
|
18
spec/command_spec.cr
Normal file
18
spec/command_spec.cr
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
require "./spec_helper"
|
||||||
|
|
||||||
|
describe Game::Command do
|
||||||
|
it "should complete build command" do
|
||||||
|
world = Game::World.new create_map_2x2
|
||||||
|
point = Game::Point.new 1, 0
|
||||||
|
building = Game::Building.new Game::Building::Type::StartPoint, "Dummy", **{
|
||||||
|
construction: Game::Construction.free 10,
|
||||||
|
}
|
||||||
|
command = Game::BuildCommand.new point, building
|
||||||
|
world.push command
|
||||||
|
tile = world.map.get point
|
||||||
|
tile.should be_a(Game::ConstructionSiteTile)
|
||||||
|
world.run 10
|
||||||
|
tile = world.map.get point
|
||||||
|
tile.should be_a(Game::BuildingTile)
|
||||||
|
end
|
||||||
|
end
|
@ -1,8 +1,29 @@
|
|||||||
require "./spec_helper"
|
require "./spec_helper"
|
||||||
|
|
||||||
|
module Test::GameResources
|
||||||
|
alias Res = Game::Resources
|
||||||
|
alias ResType = Game::Resources::Type
|
||||||
|
|
||||||
describe Game::Resources do
|
describe Game::Resources do
|
||||||
it "should be created from hash" do
|
it "should be created from hash" do
|
||||||
res = Game::Resources.new({Game::Resources::Type::Crystals => 100})
|
res = Res.new({ResType::Crystals => 100})
|
||||||
res[Game::Resources::Type::Crystals].should eq 100
|
res[ResType::Crystals].should eq 100
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should check single type" do
|
||||||
|
res = Res.new({ResType::Crystals => 100})
|
||||||
|
res.has(ResType::Crystals, 100).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should check resource bag" do
|
||||||
|
res = Res.new({ResType::Crystals => 100})
|
||||||
|
res.has({ResType::Crystals => 50}).should be_true
|
||||||
|
res.has({ResType::Crystals => 150}).should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should check empty value" do
|
||||||
|
res = Res.new
|
||||||
|
res.has({ResType::Crystals => 50}).should be_false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
74
src/game/building.cr
Normal file
74
src/game/building.cr
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
module Game
|
||||||
|
struct Production
|
||||||
|
def initialize(@ts : TimeSpan, @input : Resources, @output : Resources)
|
||||||
|
end
|
||||||
|
|
||||||
|
getter ts
|
||||||
|
getter input
|
||||||
|
getter output
|
||||||
|
end
|
||||||
|
|
||||||
|
struct Restoration
|
||||||
|
def initialize(@ts : TimeSpan, @type : Resources::Type, @cap : Capacity)
|
||||||
|
end
|
||||||
|
|
||||||
|
getter ts
|
||||||
|
getter type
|
||||||
|
getter cap
|
||||||
|
end
|
||||||
|
|
||||||
|
struct Construction
|
||||||
|
def initialize(@ts : TimeSpan, @cost : Resources, @requirements : Array(Building::Type))
|
||||||
|
end
|
||||||
|
|
||||||
|
getter ts
|
||||||
|
getter cost
|
||||||
|
getter requirements
|
||||||
|
|
||||||
|
def self.immediatly
|
||||||
|
Construction.new 0, Resources.new, [] of Building::Type
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.free(ts : TimeSpan)
|
||||||
|
Construction.new ts, Resources.new, [] of Building::Type
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Building
|
||||||
|
enum Role
|
||||||
|
Storehouse
|
||||||
|
end
|
||||||
|
|
||||||
|
enum Type
|
||||||
|
StartPoint
|
||||||
|
Storehouse
|
||||||
|
CrystalMiner
|
||||||
|
CrystalRestorer
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(
|
||||||
|
@type : Type,
|
||||||
|
@name : String,
|
||||||
|
*,
|
||||||
|
roles : Array(Role) | Nil = nil,
|
||||||
|
construction : Construction | Nil = nil,
|
||||||
|
production : Production | Nil = nil,
|
||||||
|
restoration : Restoration | Nil = nil,
|
||||||
|
storage : Capacity | Nil = nil
|
||||||
|
)
|
||||||
|
@roles = roles.nil? ? Array(Role).new : roles
|
||||||
|
@construction = construction.nil? ? Construction.immediatly : construction.as(Construction)
|
||||||
|
@production = production
|
||||||
|
@restoration = restoration
|
||||||
|
@storage = storage.nil? ? 0 : storage
|
||||||
|
end
|
||||||
|
|
||||||
|
getter type
|
||||||
|
getter name
|
||||||
|
getter roles
|
||||||
|
getter construction
|
||||||
|
getter production
|
||||||
|
getter restoration
|
||||||
|
getter storage
|
||||||
|
end
|
||||||
|
end
|
41
src/game/building_list.cr
Normal file
41
src/game/building_list.cr
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
module Game
|
||||||
|
class BuildingList
|
||||||
|
def initialize
|
||||||
|
@items = [] of NamedTuple(t: Building::Type, b: Building)
|
||||||
|
|
||||||
|
add(
|
||||||
|
Building.new Building::Type::StartPoint, "Start Point", storage: 100
|
||||||
|
)
|
||||||
|
|
||||||
|
add(
|
||||||
|
Building.new Building::Type::CrystalMiner, "Crystal Miner", **{
|
||||||
|
production: Production.new(
|
||||||
|
ts: 20,
|
||||||
|
input: Resources.new,
|
||||||
|
output: Resources.new({
|
||||||
|
Resources::Type::Crystals => 100,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
add(
|
||||||
|
Building.new Building::Type::CrystalRestorer, **{
|
||||||
|
cost: Resources.new({
|
||||||
|
Resources::Type::Crystals => 100,
|
||||||
|
}),
|
||||||
|
restoration: Restoration.new(
|
||||||
|
ts: 30,
|
||||||
|
type: Resources::Type::Crystals,
|
||||||
|
cap: 50
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
private def add(building : Building)
|
||||||
|
t = building.type
|
||||||
|
@items << {t: t, b: building}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -2,18 +2,36 @@ require "./tile"
|
|||||||
|
|
||||||
module Game
|
module Game
|
||||||
abstract class Command
|
abstract class Command
|
||||||
abstract def start(world : World) : Int32
|
abstract def start(world : World) : TimeSpan
|
||||||
abstract def finish(world : World)
|
abstract def finish(world : World)
|
||||||
abstract def desc : String
|
abstract def desc : String
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class BuildCommand < Command
|
||||||
|
def initialize(@point : Point, @building : Building)
|
||||||
|
end
|
||||||
|
|
||||||
|
def desc : String
|
||||||
|
sprintf "Building '%s'", building.name
|
||||||
|
end
|
||||||
|
|
||||||
|
def start(world : World) : TimeSpan
|
||||||
|
world.map.set(ConstructionSiteTile.new(@point))
|
||||||
|
@building.construction.ts
|
||||||
|
end
|
||||||
|
|
||||||
|
def finish(world : World)
|
||||||
|
world.map.set(BuildingTile.new(@point, @building))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class BuildCrystalHarvesterCommand < Command
|
class BuildCrystalHarvesterCommand < Command
|
||||||
BUILD_TIME = 30
|
BUILD_TIME = 30
|
||||||
|
|
||||||
def initialize(@point : Point)
|
def initialize(@point : Point)
|
||||||
end
|
end
|
||||||
|
|
||||||
def start(world : World) : Int32
|
def start(world : World) : TimeSpan
|
||||||
tile = world.map.get(@point)
|
tile = world.map.get(@point)
|
||||||
if !tile.can_build?
|
if !tile.can_build?
|
||||||
raise InvalidPlaceForBuilding.new
|
raise InvalidPlaceForBuilding.new
|
||||||
@ -41,7 +59,7 @@ module Game
|
|||||||
@value = 0
|
@value = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def start(world : World) : Int32
|
def start(world : World) : TimeSpan
|
||||||
deposit_tile = nearest_deposit(world)
|
deposit_tile = nearest_deposit(world)
|
||||||
stock_tile = nearest_stock(world)
|
stock_tile = nearest_stock(world)
|
||||||
if deposit_tile && stock_tile
|
if deposit_tile && stock_tile
|
||||||
@ -83,7 +101,7 @@ module Game
|
|||||||
def initialize(@point : Point)
|
def initialize(@point : Point)
|
||||||
end
|
end
|
||||||
|
|
||||||
def start(world : World) : Int32
|
def start(world : World) : TimeSpan
|
||||||
tile = world.map.get(@point)
|
tile = world.map.get(@point)
|
||||||
if !tile.can_build?
|
if !tile.can_build?
|
||||||
raise InvalidPlaceForBuilding.new
|
raise InvalidPlaceForBuilding.new
|
||||||
@ -113,7 +131,7 @@ module Game
|
|||||||
def initialize(@point : Point)
|
def initialize(@point : Point)
|
||||||
end
|
end
|
||||||
|
|
||||||
def start(world : World) : Int32
|
def start(world : World) : TimeSpan
|
||||||
@target_tile = nearest_deposit(world)
|
@target_tile = nearest_deposit(world)
|
||||||
if @target_tile
|
if @target_tile
|
||||||
dist = @point.distance(@target_tile.as(Tile).point)
|
dist = @point.distance(@target_tile.as(Tile).point)
|
||||||
@ -148,7 +166,7 @@ module Game
|
|||||||
def initialize(@point : Point)
|
def initialize(@point : Point)
|
||||||
end
|
end
|
||||||
|
|
||||||
def start(world : World) : Int32
|
def start(world : World) : TimeSpan
|
||||||
tile = world.map.get(@point)
|
tile = world.map.get(@point)
|
||||||
if !tile.can_build?
|
if !tile.can_build?
|
||||||
raise InvalidPlaceForBuilding.new
|
raise InvalidPlaceForBuilding.new
|
||||||
@ -178,7 +196,7 @@ module Game
|
|||||||
@can_terr = false
|
@can_terr = false
|
||||||
end
|
end
|
||||||
|
|
||||||
def start(world : World) : Int32
|
def start(world : World) : TimeSpan
|
||||||
if world.resources.has(Resources::Type::Crystals, CRYSTAL_REQUIRED)
|
if world.resources.has(Resources::Type::Crystals, CRYSTAL_REQUIRED)
|
||||||
world.resources.dec(Resources::Type::Crystals, CRYSTAL_REQUIRED)
|
world.resources.dec(Resources::Type::Crystals, CRYSTAL_REQUIRED)
|
||||||
@can_terr = true
|
@can_terr = true
|
||||||
|
5
src/game/game.cr
Normal file
5
src/game/game.cr
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module Game
|
||||||
|
alias TimePoint = Int64
|
||||||
|
alias TimeSpan = Int32 | Int64
|
||||||
|
alias Capacity = Int32
|
||||||
|
end
|
@ -8,12 +8,17 @@ class Game::Resources
|
|||||||
|
|
||||||
alias Capacity = Int32
|
alias Capacity = Int32
|
||||||
|
|
||||||
def initialize
|
alias ResourceBag = Hash(Type, Capacity)
|
||||||
@values = {} of Type => Capacity
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(vals : Hash(Type, Capacity))
|
def initialize(vals : ResourceBag | Nil = nil)
|
||||||
@values = vals.clone
|
@values = {} of Type => Capacity
|
||||||
|
Type.each { |t, v| @values[t] = 0 }
|
||||||
|
if vals.is_a?(ResourceBag)
|
||||||
|
vals.each do |i|
|
||||||
|
t, v = i
|
||||||
|
@values[t] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def [](t : Type)
|
def [](t : Type)
|
||||||
@ -21,7 +26,15 @@ class Game::Resources
|
|||||||
end
|
end
|
||||||
|
|
||||||
def has(t : Type, value : Capacity) : Bool
|
def has(t : Type, value : Capacity) : Bool
|
||||||
@values.fetch(t, 0) >= value
|
@values[t] >= value
|
||||||
|
end
|
||||||
|
|
||||||
|
def has(vs : ResourceBag) : Bool
|
||||||
|
vs.reduce true do |acc, entry|
|
||||||
|
t = entry[0]
|
||||||
|
v = entry[1]
|
||||||
|
acc && @values[t] >= v
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def inc(t : Type, value : Capacity)
|
def inc(t : Type, value : Capacity)
|
||||||
@ -32,6 +45,14 @@ class Game::Resources
|
|||||||
@values[t] = new_value
|
@values[t] = new_value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def inc?(vs : ResourceBag) : Bool
|
||||||
|
false unless has(vs)
|
||||||
|
vs.each do |t, c|
|
||||||
|
@values[t] = @values[t] + c
|
||||||
|
end
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
def dec(t : Type, value : Capacity)
|
def dec(t : Type, value : Capacity)
|
||||||
inc(t, -value)
|
inc(t, -value)
|
||||||
end
|
end
|
||||||
|
@ -7,6 +7,7 @@ module Game
|
|||||||
Plateau
|
Plateau
|
||||||
Terraformer
|
Terraformer
|
||||||
Warehouse
|
Warehouse
|
||||||
|
Building
|
||||||
end
|
end
|
||||||
|
|
||||||
abstract class Tile
|
abstract class Tile
|
||||||
@ -20,9 +21,12 @@ module Game
|
|||||||
getter cap
|
getter cap
|
||||||
getter cur
|
getter cur
|
||||||
|
|
||||||
abstract def letter : Char
|
|
||||||
abstract def has_role(role : TileRole) : Bool
|
abstract def has_role(role : TileRole) : Bool
|
||||||
|
|
||||||
|
def letter : Char
|
||||||
|
' '
|
||||||
|
end
|
||||||
|
|
||||||
def withdraw(value)
|
def withdraw(value)
|
||||||
if value >= @cur
|
if value >= @cur
|
||||||
wd = @cur
|
wd = @cur
|
||||||
@ -118,4 +122,15 @@ module Game
|
|||||||
role == TileRole::Terraformer
|
role == TileRole::Terraformer
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class BuildingTile < Tile
|
||||||
|
def initialize(@point : Point, @building : Building)
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_role(role : TileRole) : Bool
|
||||||
|
role == TileRole::Building
|
||||||
|
end
|
||||||
|
|
||||||
|
getter building
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
require "./resources"
|
require "./resources"
|
||||||
|
|
||||||
class Game::World
|
class Game::World
|
||||||
property ts : Int64
|
property ts : TimePoint
|
||||||
|
|
||||||
def initialize(@map : Map, @ts = 0_i64)
|
def initialize(@map : Map, @ts : TimePoint = 0_i64)
|
||||||
@start_ts = @ts
|
@start_ts = @ts
|
||||||
@resources = Resources.new
|
@resources = Resources.new
|
||||||
@queue = Queue.new
|
@queue = Queue.new
|
||||||
@ -23,7 +23,7 @@ class Game::World
|
|||||||
@queue.push(done_at, command)
|
@queue.push(done_at, command)
|
||||||
end
|
end
|
||||||
|
|
||||||
def run(ts : Int64)
|
def run(ts : TimePoint)
|
||||||
loop do
|
loop do
|
||||||
item = @queue.pop(ts)
|
item = @queue.pop(ts)
|
||||||
if item.nil?
|
if item.nil?
|
||||||
|
Loading…
Reference in New Issue
Block a user