Everything broken, restore tests

This commit is contained in:
Anton Vakhrushev 2019-10-12 17:52:39 +03:00
parent dacecf2aa8
commit 658d3b7288
9 changed files with 308 additions and 259 deletions

View File

@ -1,21 +1,3 @@
require "./spec_helper" require "./spec_helper"
describe "World" do # @todo World tests
it "should build crystal harvester" do
world = Game::World.new create_map_2x2
point = Game::Point.new 1, 0
cmd = Game::BuildCrystalHarvesterCommand.new(point)
world.push(cmd)
world.run(100)
world.map.get(point).has_role(Game::TileRole::CrystalHarvester)
end
it "should fail when not enought resources" do
world = Game::World.new create_map_2x2
point = Game::Point.new 1, 0
cmd = Game::BuildCrystalRestorerCommand.new(point)
expect_raises(Game::NotEnoughtResources) do
world.push(cmd)
end
end
end

View File

@ -5,29 +5,21 @@ require "./cli/**"
map = Game::Generator.make 5, 5 map = Game::Generator.make 5, 5
ts = Time.local.to_unix ts = Time.local.to_unix
world = Game::World.new map, ts world = Game::World.new map, ts
buildings = Game::BuildingFactory.new
router = CLI::CommandRouter.new router = CLI::CommandRouter.new
router.add "harv {x} {y}", "Build harvester at x,y (+80cr/10s)" do |p| buildings.items.each do |i|
t = i[:t]
b = i[:b]
route = sprintf "%s {x} {y}", b.name.downcase
desc = sprintf "Build %s at x,y", b.name
router.add route, desc do |p|
x = p["x"].to_i32 x = p["x"].to_i32
y = p["y"].to_i32 y = p["y"].to_i32
point = Game::Point.new(x, y) point = Game::Point.new(x, y)
world.push(Game::BuildCrystalHarvesterCommand.new(point)) world.push(Game::BuildCommand.new(point, b))
printf "Build harvester at %d %d\n", x, y printf "Build %s at %d %d\n", b.name, x, y
end end
router.add "rest {x} {y}", "Build restorer at x,y (100 cr, +30cr/15s)" do |p|
x = p["x"].to_i32
y = p["y"].to_i32
point = Game::Point.new(x, y)
world.push(Game::BuildCrystalRestorerCommand.new(point))
end
router.add "terr {x} {y}", "Build terraformator at x,y (300 cr, +5terr/-50cr/60s)" do |p|
x = p["x"].to_i32
y = p["y"].to_i32
point = Game::Point.new(x, y)
world.push(Game::BuildTerraformerCommand.new(point))
end end
router.add "help", "Show all commands" do |p| router.add "help", "Show all commands" do |p|

View File

@ -8,12 +8,13 @@ module Game
getter output getter output
end end
class Restoration class Mining
def initialize(@ts : TimeSpan, @type : Resources::Type, @cap : Capacity) def initialize(@ts : TimeSpan, @input : Resources, @res : Resources::Type, @cap : Capacity)
end end
getter ts getter ts
getter type getter input
getter res
getter cap getter cap
end end
@ -44,6 +45,7 @@ module Game
Storehouse Storehouse
CrystalMiner CrystalMiner
CrystalRestorer CrystalRestorer
Terraformer
end end
def initialize( def initialize(
@ -53,12 +55,14 @@ module Game
roles : Array(Role) | Nil = nil, roles : Array(Role) | Nil = nil,
construction : Construction | Nil = nil, construction : Construction | Nil = nil,
production : Production | Nil = nil, production : Production | Nil = nil,
restoration : Restoration | Nil = nil, mining : Mining | Nil = nil,
restoration : Mining | Nil = nil,
storage : Capacity | Nil = nil storage : Capacity | Nil = nil
) )
@roles = roles.nil? ? Array(Role).new : roles @roles = roles.nil? ? Array(Role).new : roles
@construction = construction.nil? ? Construction.immediatly : construction.as(Construction) @construction = construction.nil? ? Construction.immediatly : construction.as(Construction)
@production = production @production = production
@mining = mining
@restoration = restoration @restoration = restoration
@storage = storage.nil? ? 0 : storage @storage = storage.nil? ? 0 : storage
end end
@ -68,6 +72,7 @@ module Game
getter roles getter roles
getter construction getter construction
getter production getter production
getter mining
getter restoration getter restoration
getter storage getter storage
end end

View File

@ -0,0 +1,68 @@
module Game
class BuildingFactory
def initialize
@items = [] of NamedTuple(t: Building::Type, b: Building)
add(
Building.new Building::Type::StartPoint, "Start", storage: 100
)
add(
Building.new Building::Type::CrystalMiner, "Miner", **{
mining: Production.new(
ts: 20,
input: Resources.new,
res: Resources::Type::Crystals,
cap: 40,
),
}
)
add(
Building.new Building::Type::CrystalRestorer, "Restorer", **{
construction: Construction.new(
ts: 30,
cost: Resources.new({
Resources::Type::Crystals => 100,
}),
requirements: [] of Game::Building::Type
),
restoration: Restoration.new(
ts: 30,
input: Resources.new,
res: Resources::Type::Crystals,
cap: 20
),
}
)
add(
Building.new Building::Type::Terraformer, "Terraformator", **{
construction: Construction.new(
ts: 120,
cost: Resources.new({
Resources::Type::Crystals => 300,
}),
requirements: [] of Game::Building::Type
),
production: Production.new(
ts: 60,
input: Resources.new({
Resources::Type::Crystals => 50,
}),
output: Resources.new({
Resources::Type::Terraformation => 5,
})
),
}
)
end
getter items
private def add(building : Building)
t = building.type
@items << {t: t, b: building}
end
end
end

View File

@ -1,41 +0,0 @@
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

View File

@ -12,7 +12,7 @@ module Game
end end
def desc : String def desc : String
sprintf "Building '%s'", building.name sprintf "Building '%s'", @building.name
end end
def start(world : World) : TimeSpan def start(world : World) : TimeSpan
@ -27,199 +27,238 @@ module Game
def finish(world : World) def finish(world : World)
world.map.set(BuildingTile.new(@point, @building)) world.map.set(BuildingTile.new(@point, @building))
# world.push(MineCommand.new(@point))
end end
end end
class BuildCrystalHarvesterCommand < Command # class MineCommand < Command
BUILD_TIME = 30 # @output : Resources | Nil
def initialize(@point : Point) # def initialize(@point : Point)
end # end
def start(world : World) : TimeSpan # def start(world : World) : TimeSpan
tile = world.map.get(@point) # tile = world.map.get(@point).as(BuildingTile)
if !tile.can_build? # building = tile.building
raise InvalidPlaceForBuilding.new # production = building.production
end # if !world.resources.has(production.input)
world.map.set(ConstructionSiteTile.new(@point)) # return production.ts
BUILD_TIME # end
end # target_resource = production.type
# world.resources.dec(production.input)
# @output = production.output
# production.ts
# end
def finish(world : World) # def finish(world : World)
world.map.set(CrystalHarvesterTile.new(@point)) # if @output.nil?
world.push(HarvestCrystalCommand.new(@point)) # return
end # end
# world.resources.inc(output)
# world.push(MineCommand.new(@point))
# end
def desc : String # def desc : String
sprintf "Build harvester site at %d,%d", @point.x, @point.y # sprintf "Build harvester site at %d,%d", @point.x, @point.y
end # end
end
class HarvestCrystalCommand < Command # # private def nearest_deposit(world : World, res : Resources::Type)
HARVEST_VALUE = 80 # # world.map.nearest_tile @point do |tile|
HARVEST_TIME = 10 # # tile.has_role(TileRole::Deposit) && tile.cur > 0
REST_TIME = 5 # # end
# # end
# end
def initialize(@point : Point) # class BuildCrystalHarvesterCommand < Command
@value = 0 # BUILD_TIME = 30
end
def start(world : World) : TimeSpan # def initialize(@point : Point)
deposit_tile = nearest_deposit(world) # end
stock_tile = nearest_stock(world)
if deposit_tile && stock_tile
wood_dist = @point.distance(deposit_tile.point)
stock_dist = @point.distance(stock_tile.point)
@value = deposit_tile.withdraw(HARVEST_VALUE)
HARVEST_TIME + 2 * wood_dist + 2 * stock_dist
else
REST_TIME
end
end
def finish(world : World) # def start(world : World) : TimeSpan
world.resources.inc(Resources::Type::Crystals, @value) # tile = world.map.get(@point)
world.push(HarvestCrystalCommand.new(@point)) # if !tile.can_build?
end # raise InvalidPlaceForBuilding.new
# end
# world.map.set(ConstructionSiteTile.new(@point))
# BUILD_TIME
# end
def desc : String # def finish(world : World)
sprintf "Harvest crystals at %d,%d", @point.x, @point.y # world.map.set(CrystalHarvesterTile.new(@point))
end # world.push(HarvestCrystalCommand.new(@point))
# end
private def nearest_deposit(world : World) # def desc : String
world.map.nearest_tile @point do |tile| # sprintf "Build harvester site at %d,%d", @point.x, @point.y
tile.has_role(TileRole::CrystalDeposits) && tile.cur > 0 # end
end # end
end
private def nearest_stock(world : World) # class HarvestCrystalCommand < Command
world.map.nearest_tile @point do |tile| # HARVEST_VALUE = 80
tile.has_role(TileRole::Warehouse) # HARVEST_TIME = 10
end # REST_TIME = 5
end
end
class BuildCrystalRestorerCommand < Command # def initialize(@point : Point)
CRYSTALS_COST = 100 # @value = 0
BUILD_TIME = 50 # end
def initialize(@point : Point) # def start(world : World) : TimeSpan
end # deposit_tile = nearest_deposit(world)
# stock_tile = nearest_stock(world)
# if deposit_tile && stock_tile
# wood_dist = @point.distance(deposit_tile.point)
# stock_dist = @point.distance(stock_tile.point)
# @value = deposit_tile.withdraw(HARVEST_VALUE)
# HARVEST_TIME + 2 * wood_dist + 2 * stock_dist
# else
# REST_TIME
# end
# end
def start(world : World) : TimeSpan # def finish(world : World)
tile = world.map.get(@point) # world.resources.inc(Resources::Type::Crystals, @value)
if !tile.can_build? # world.push(HarvestCrystalCommand.new(@point))
raise InvalidPlaceForBuilding.new # end
end
world.resources.dec(Resources::Type::Crystals, CRYSTALS_COST)
world.map.set(ConstructionSiteTile.new(@point))
BUILD_TIME
end
def finish(world : World) # def desc : String
world.map.set(CrystalRestorerTile.new(@point)) # sprintf "Harvest crystals at %d,%d", @point.x, @point.y
world.push(RestoreCrystalCommand.new(@point)) # end
end
def desc : String # private def nearest_deposit(world : World)
sprintf "Build crystal restorer at %d,%d", @point.x, @point.y # # world.map.nearest_tile @point do |tile|
end # # tile.has_role(TileRole::CrystalDeposits) && tile.cur > 0
end # # end
# end
class RestoreCrystalCommand < Command # private def nearest_stock(world : World)
RESTORE_TIME = 15 # # world.map.nearest_tile @point do |tile|
RESTORE_VALUE = 30 # # tile.has_role(TileRole::Warehouse)
REST_TIME = 5 # # end
# end
# end
@target_tile : Tile | Nil = nil # class BuildCrystalRestorerCommand < Command
# CRYSTALS_COST = 100
# BUILD_TIME = 50
def initialize(@point : Point) # def initialize(@point : Point)
end # end
def start(world : World) : TimeSpan # def start(world : World) : TimeSpan
@target_tile = nearest_deposit(world) # tile = world.map.get(@point)
if @target_tile # if !tile.can_build?
dist = @point.distance(@target_tile.as(Tile).point) # raise InvalidPlaceForBuilding.new
RESTORE_TIME + 2 * dist # end
else # world.resources.dec(Resources::Type::Crystals, CRYSTALS_COST)
REST_TIME # world.map.set(ConstructionSiteTile.new(@point))
end # BUILD_TIME
end # end
def finish(world : World) # def finish(world : World)
if @target_tile # world.map.set(CrystalRestorerTile.new(@point))
@target_tile.as(Tile).charge(RESTORE_VALUE) # world.push(RestoreCrystalCommand.new(@point))
end # end
world.push(RestoreCrystalCommand.new(@point))
end
def desc : String # def desc : String
sprintf "Restore crystals at %d,%d", @point.x, @point.y # sprintf "Build crystal restorer at %d,%d", @point.x, @point.y
end # end
# end
private def nearest_deposit(world : World) # class RestoreCrystalCommand < Command
world.map.nearest_tile @point do |tile| # RESTORE_TIME = 15
tile.has_role(TileRole::CrystalDeposits) && tile.cur < tile.cap # RESTORE_VALUE = 30
end # REST_TIME = 5
end
end
class BuildTerraformerCommand < Command # @target_tile : Tile | Nil = nil
CRYSTALS_COST = 300
BUILD_TIME = 120
def initialize(@point : Point) # def initialize(@point : Point)
end # end
def start(world : World) : TimeSpan # def start(world : World) : TimeSpan
tile = world.map.get(@point) # @target_tile = nearest_deposit(world)
if !tile.can_build? # if @target_tile
raise InvalidPlaceForBuilding.new # dist = @point.distance(@target_tile.as(Tile).point)
end # RESTORE_TIME + 2 * dist
world.resources.dec(Resources::Type::Crystals, CRYSTALS_COST) # else
world.map.set(ConstructionSiteTile.new(@point)) # REST_TIME
BUILD_TIME # end
end # end
def finish(world : World) # def finish(world : World)
world.map.set(TerraformerTile.new(@point)) # if @target_tile
world.push(TerraformCommand.new(@point)) # @target_tile.as(Tile).charge(RESTORE_VALUE)
end # end
# world.push(RestoreCrystalCommand.new(@point))
# end
def desc : String # def desc : String
sprintf "Build terraformer at %d,%d", @point.x, @point.y # sprintf "Restore crystals at %d,%d", @point.x, @point.y
end # end
end
class TerraformCommand < Command # private def nearest_deposit(world : World)
PRODUCTION_TIME = 60 # world.map.nearest_tile @point do |tile|
REST_TIME = 20 # tile.has_role(TileRole::CrystalDeposits) && tile.cur < tile.cap
PRODUCTION_VALUE = 5 # end
CRYSTAL_REQUIRED = 50 # end
# end
def initialize(@point : Point) # class BuildTerraformerCommand < Command
@can_terr = false # CRYSTALS_COST = 300
end # BUILD_TIME = 120
def start(world : World) : TimeSpan # def initialize(@point : Point)
if world.resources.has(Resources::Type::Crystals, CRYSTAL_REQUIRED) # end
world.resources.dec(Resources::Type::Crystals, CRYSTAL_REQUIRED)
@can_terr = true
PRODUCTION_TIME
else
REST_TIME
end
end
def desc : String # def start(world : World) : TimeSpan
"Terraform planet" # tile = world.map.get(@point)
end # if !tile.can_build?
# raise InvalidPlaceForBuilding.new
# end
# world.resources.dec(Resources::Type::Crystals, CRYSTALS_COST)
# world.map.set(ConstructionSiteTile.new(@point))
# BUILD_TIME
# end
def finish(world : World) # def finish(world : World)
if @can_terr # world.map.set(TerraformerTile.new(@point))
world.resources.inc(Resources::Type::Terraformation, PRODUCTION_VALUE) # world.push(TerraformCommand.new(@point))
end # end
world.push(TerraformCommand.new(@point))
end # def desc : String
end # sprintf "Build terraformer at %d,%d", @point.x, @point.y
# end
# end
# class TerraformCommand < Command
# PRODUCTION_TIME = 60
# REST_TIME = 20
# PRODUCTION_VALUE = 5
# CRYSTAL_REQUIRED = 50
# def initialize(@point : Point)
# @can_terr = false
# end
# def start(world : World) : TimeSpan
# if world.resources.has(Resources::Type::Crystals, CRYSTAL_REQUIRED)
# world.resources.dec(Resources::Type::Crystals, CRYSTAL_REQUIRED)
# @can_terr = true
# PRODUCTION_TIME
# else
# REST_TIME
# end
# end
# def desc : String
# "Terraform planet"
# end
# def finish(world : World)
# if @can_terr
# world.resources.inc(Resources::Type::Terraformation, PRODUCTION_VALUE)
# end
# world.push(TerraformCommand.new(@point))
# end
# end
end end

View File

@ -7,4 +7,7 @@ module Game
class InvalidPlaceForBuilding < BaseException class InvalidPlaceForBuilding < BaseException
end end
class ResourceMismatch < BaseException
end
end end

View File

@ -1,6 +1,6 @@
class Game::Queue class Game::Queue
struct Item struct Item
def initialize(@ts : Int64, @command : Command) def initialize(@ts : TimePoint, @command : Command)
end end
getter ts getter ts
@ -11,7 +11,7 @@ class Game::Queue
@data = [] of Item @data = [] of Item
end end
def push(ts : Int64, value : Command) def push(ts : TimePoint, value : Command)
# very unoptimal algo # very unoptimal algo
@data.push(Item.new(ts, value)) @data.push(Item.new(ts, value))
@data.sort! do |a, b| @data.sort! do |a, b|
@ -19,7 +19,7 @@ class Game::Queue
end end
end end
def pop(ts : Int64) : Item | Nil def pop(ts : TimePoint) : Item | Nil
if @data.size == 0 if @data.size == 0
return nil return nil
end end

View File

@ -1,15 +1,4 @@
module Game module Game
enum TileRole
ConstructionSite
CrystalDeposits
CrystalHarvester
CrystalRestorer
Plateau
Terraformer
Warehouse
Building
end
abstract class Tile abstract class Tile
property cap : Int32 = 0 property cap : Int32 = 0
property cur : Int32 = 0 property cur : Int32 = 0
@ -133,4 +122,16 @@ module Game
getter building getter building
end end
class DepositTile < Tile
def initialize(@point : Point, @res : Resources::Type, @cap : Capacity)
end
def has_role(role : TileRole) : Bool
role == TileRole::Deposit
end
getter res
getter cap
end
end end