From 658d3b72886e51b52d02e4608a00167198ba3e2b Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Sat, 12 Oct 2019 17:52:39 +0300 Subject: [PATCH] Everything broken, restore tests --- spec/world_spec.cr | 20 +- src/expansion.cr | 34 ++-- src/game/building.cr | 13 +- src/game/building_factory.cr | 68 +++++++ src/game/building_list.cr | 41 ---- src/game/command.cr | 359 +++++++++++++++++++---------------- src/game/exception.cr | 3 + src/game/queue.cr | 6 +- src/game/tile.cr | 23 +-- 9 files changed, 308 insertions(+), 259 deletions(-) create mode 100644 src/game/building_factory.cr delete mode 100644 src/game/building_list.cr diff --git a/spec/world_spec.cr b/spec/world_spec.cr index 5fdbf2f..cf0b670 100644 --- a/spec/world_spec.cr +++ b/spec/world_spec.cr @@ -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 diff --git a/src/expansion.cr b/src/expansion.cr index 0de3f69..6bff100 100644 --- a/src/expansion.cr +++ b/src/expansion.cr @@ -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| - 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 -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)) +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::BuildCommand.new(point, b)) + printf "Build %s at %d %d\n", b.name, x, y + end end router.add "help", "Show all commands" do |p| diff --git a/src/game/building.cr b/src/game/building.cr index dc6e40d..4dc12db 100644 --- a/src/game/building.cr +++ b/src/game/building.cr @@ -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 diff --git a/src/game/building_factory.cr b/src/game/building_factory.cr new file mode 100644 index 0000000..9ca4649 --- /dev/null +++ b/src/game/building_factory.cr @@ -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 diff --git a/src/game/building_list.cr b/src/game/building_list.cr deleted file mode 100644 index 1b7dcb0..0000000 --- a/src/game/building_list.cr +++ /dev/null @@ -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 diff --git a/src/game/command.cr b/src/game/command.cr index f7bcd9d..a6b05bb 100644 --- a/src/game/command.cr +++ b/src/game/command.cr @@ -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 diff --git a/src/game/exception.cr b/src/game/exception.cr index ba1922f..e367282 100644 --- a/src/game/exception.cr +++ b/src/game/exception.cr @@ -7,4 +7,7 @@ module Game class InvalidPlaceForBuilding < BaseException end + + class ResourceMismatch < BaseException + end end diff --git a/src/game/queue.cr b/src/game/queue.cr index 9597490..1e3591c 100644 --- a/src/game/queue.cr +++ b/src/game/queue.cr @@ -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 diff --git a/src/game/tile.cr b/src/game/tile.cr index 9b07243..4449406 100644 --- a/src/game/tile.cr +++ b/src/game/tile.cr @@ -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