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"
describe "World" do
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
# @todo World tests

View File

@ -5,29 +5,21 @@ require "./cli/**"
map = Game::Generator.make 5, 5
ts = Time.local.to_unix
world = Game::World.new map, ts
buildings = Game::BuildingFactory.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
y = p["y"].to_i32
point = Game::Point.new(x, y)
world.push(Game::BuildCrystalHarvesterCommand.new(point))
printf "Build harvester at %d %d\n", x, y
world.push(Game::BuildCommand.new(point, b))
printf "Build %s at %d %d\n", b.name, x, y
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
router.add "help", "Show all commands" do |p|

View File

@ -8,12 +8,13 @@ module Game
getter output
end
class Restoration
def initialize(@ts : TimeSpan, @type : Resources::Type, @cap : Capacity)
class Mining
def initialize(@ts : TimeSpan, @input : Resources, @res : Resources::Type, @cap : Capacity)
end
getter ts
getter type
getter input
getter res
getter cap
end
@ -44,6 +45,7 @@ module Game
Storehouse
CrystalMiner
CrystalRestorer
Terraformer
end
def initialize(
@ -53,12 +55,14 @@ module Game
roles : Array(Role) | Nil = nil,
construction : Construction | Nil = nil,
production : Production | Nil = nil,
restoration : Restoration | Nil = nil,
mining : Mining | Nil = nil,
restoration : Mining | Nil = nil,
storage : Capacity | Nil = nil
)
@roles = roles.nil? ? Array(Role).new : roles
@construction = construction.nil? ? Construction.immediatly : construction.as(Construction)
@production = production
@mining = mining
@restoration = restoration
@storage = storage.nil? ? 0 : storage
end
@ -68,6 +72,7 @@ module Game
getter roles
getter construction
getter production
getter mining
getter restoration
getter storage
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
def desc : String
sprintf "Building '%s'", building.name
sprintf "Building '%s'", @building.name
end
def start(world : World) : TimeSpan
@ -27,199 +27,238 @@ module Game
def finish(world : World)
world.map.set(BuildingTile.new(@point, @building))
# world.push(MineCommand.new(@point))
end
end
class BuildCrystalHarvesterCommand < Command
BUILD_TIME = 30
# class MineCommand < Command
# @output : Resources | Nil
def initialize(@point : Point)
end
# def initialize(@point : Point)
# end
def start(world : World) : TimeSpan
tile = world.map.get(@point)
if !tile.can_build?
raise InvalidPlaceForBuilding.new
end
world.map.set(ConstructionSiteTile.new(@point))
BUILD_TIME
end
# def start(world : World) : TimeSpan
# tile = world.map.get(@point).as(BuildingTile)
# building = tile.building
# production = building.production
# if !world.resources.has(production.input)
# return production.ts
# end
# target_resource = production.type
# world.resources.dec(production.input)
# @output = production.output
# production.ts
# end
def finish(world : World)
world.map.set(CrystalHarvesterTile.new(@point))
world.push(HarvestCrystalCommand.new(@point))
end
# def finish(world : World)
# if @output.nil?
# return
# end
# world.resources.inc(output)
# world.push(MineCommand.new(@point))
# end
def desc : String
sprintf "Build harvester site at %d,%d", @point.x, @point.y
end
end
# def desc : String
# sprintf "Build harvester site at %d,%d", @point.x, @point.y
# end
class HarvestCrystalCommand < Command
HARVEST_VALUE = 80
HARVEST_TIME = 10
REST_TIME = 5
# # private def nearest_deposit(world : World, res : Resources::Type)
# # world.map.nearest_tile @point do |tile|
# # tile.has_role(TileRole::Deposit) && tile.cur > 0
# # end
# # end
# end
def initialize(@point : Point)
@value = 0
end
# class BuildCrystalHarvesterCommand < Command
# BUILD_TIME = 30
def start(world : World) : TimeSpan
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 initialize(@point : Point)
# end
def finish(world : World)
world.resources.inc(Resources::Type::Crystals, @value)
world.push(HarvestCrystalCommand.new(@point))
end
# def start(world : World) : TimeSpan
# tile = world.map.get(@point)
# if !tile.can_build?
# raise InvalidPlaceForBuilding.new
# end
# world.map.set(ConstructionSiteTile.new(@point))
# BUILD_TIME
# end
def desc : String
sprintf "Harvest crystals at %d,%d", @point.x, @point.y
end
# def finish(world : World)
# world.map.set(CrystalHarvesterTile.new(@point))
# world.push(HarvestCrystalCommand.new(@point))
# end
private def nearest_deposit(world : World)
world.map.nearest_tile @point do |tile|
tile.has_role(TileRole::CrystalDeposits) && tile.cur > 0
end
end
# def desc : String
# sprintf "Build harvester site at %d,%d", @point.x, @point.y
# end
# end
private def nearest_stock(world : World)
world.map.nearest_tile @point do |tile|
tile.has_role(TileRole::Warehouse)
end
end
end
# class HarvestCrystalCommand < Command
# HARVEST_VALUE = 80
# HARVEST_TIME = 10
# REST_TIME = 5
class BuildCrystalRestorerCommand < Command
CRYSTALS_COST = 100
BUILD_TIME = 50
# def initialize(@point : Point)
# @value = 0
# end
def initialize(@point : Point)
end
# def start(world : World) : TimeSpan
# 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
tile = world.map.get(@point)
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)
# world.resources.inc(Resources::Type::Crystals, @value)
# world.push(HarvestCrystalCommand.new(@point))
# end
def finish(world : World)
world.map.set(CrystalRestorerTile.new(@point))
world.push(RestoreCrystalCommand.new(@point))
end
# def desc : String
# sprintf "Harvest crystals at %d,%d", @point.x, @point.y
# end
def desc : String
sprintf "Build crystal restorer at %d,%d", @point.x, @point.y
end
end
# private def nearest_deposit(world : World)
# # world.map.nearest_tile @point do |tile|
# # tile.has_role(TileRole::CrystalDeposits) && tile.cur > 0
# # end
# end
class RestoreCrystalCommand < Command
RESTORE_TIME = 15
RESTORE_VALUE = 30
REST_TIME = 5
# private def nearest_stock(world : World)
# # world.map.nearest_tile @point do |tile|
# # tile.has_role(TileRole::Warehouse)
# # end
# end
# end
@target_tile : Tile | Nil = nil
# class BuildCrystalRestorerCommand < Command
# CRYSTALS_COST = 100
# BUILD_TIME = 50
def initialize(@point : Point)
end
# def initialize(@point : Point)
# end
def start(world : World) : TimeSpan
@target_tile = nearest_deposit(world)
if @target_tile
dist = @point.distance(@target_tile.as(Tile).point)
RESTORE_TIME + 2 * dist
else
REST_TIME
end
end
# def start(world : World) : TimeSpan
# tile = world.map.get(@point)
# 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)
if @target_tile
@target_tile.as(Tile).charge(RESTORE_VALUE)
end
world.push(RestoreCrystalCommand.new(@point))
end
# def finish(world : World)
# world.map.set(CrystalRestorerTile.new(@point))
# world.push(RestoreCrystalCommand.new(@point))
# end
def desc : String
sprintf "Restore crystals at %d,%d", @point.x, @point.y
end
# def desc : String
# sprintf "Build crystal restorer at %d,%d", @point.x, @point.y
# end
# end
private def nearest_deposit(world : World)
world.map.nearest_tile @point do |tile|
tile.has_role(TileRole::CrystalDeposits) && tile.cur < tile.cap
end
end
end
# class RestoreCrystalCommand < Command
# RESTORE_TIME = 15
# RESTORE_VALUE = 30
# REST_TIME = 5
class BuildTerraformerCommand < Command
CRYSTALS_COST = 300
BUILD_TIME = 120
# @target_tile : Tile | Nil = nil
def initialize(@point : Point)
end
# def initialize(@point : Point)
# end
def start(world : World) : TimeSpan
tile = world.map.get(@point)
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 start(world : World) : TimeSpan
# @target_tile = nearest_deposit(world)
# if @target_tile
# dist = @point.distance(@target_tile.as(Tile).point)
# RESTORE_TIME + 2 * dist
# else
# REST_TIME
# end
# end
def finish(world : World)
world.map.set(TerraformerTile.new(@point))
world.push(TerraformCommand.new(@point))
end
# def finish(world : World)
# if @target_tile
# @target_tile.as(Tile).charge(RESTORE_VALUE)
# end
# world.push(RestoreCrystalCommand.new(@point))
# end
def desc : String
sprintf "Build terraformer at %d,%d", @point.x, @point.y
end
end
# def desc : String
# sprintf "Restore crystals at %d,%d", @point.x, @point.y
# end
class TerraformCommand < Command
PRODUCTION_TIME = 60
REST_TIME = 20
PRODUCTION_VALUE = 5
CRYSTAL_REQUIRED = 50
# private def nearest_deposit(world : World)
# world.map.nearest_tile @point do |tile|
# tile.has_role(TileRole::CrystalDeposits) && tile.cur < tile.cap
# end
# end
# end
def initialize(@point : Point)
@can_terr = false
end
# class BuildTerraformerCommand < Command
# CRYSTALS_COST = 300
# BUILD_TIME = 120
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 initialize(@point : Point)
# end
def desc : String
"Terraform planet"
end
# def start(world : World) : TimeSpan
# tile = world.map.get(@point)
# 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)
if @can_terr
world.resources.inc(Resources::Type::Terraformation, PRODUCTION_VALUE)
end
world.push(TerraformCommand.new(@point))
end
end
# def finish(world : World)
# world.map.set(TerraformerTile.new(@point))
# world.push(TerraformCommand.new(@point))
# end
# def desc : String
# 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

View File

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

View File

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

View File

@ -1,15 +1,4 @@
module Game
enum TileRole
ConstructionSite
CrystalDeposits
CrystalHarvester
CrystalRestorer
Plateau
Terraformer
Warehouse
Building
end
abstract class Tile
property cap : Int32 = 0
property cur : Int32 = 0
@ -133,4 +122,16 @@ module Game
getter building
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