Breeder in CW3

Started by rytan451, September 10, 2018, 08:50:13 AM

Previous topic - Next topic

rytan451

So I saw the most recent of Virgil's videos (if you're seeing this, Virgil, it looks great as always.  The gameplay, that is; I think that's what we're all concerned about).  I thought to myself that I could probably duplicate the Breeder mechanic with CRPL, so I did.

It was, to say the least, slow. 

I have two questions:

  • As I said, it's slow. Does anyone have any suggestions on how to fix that?
  • After checking in the editor, it seems that a ton of CRPL Cores with "DigiSupport.crpl" attached appeared. The expected behaviour is for each continuous area of digitalis would have a single support. Any suggestions on how to fix this?

I think the reason for the multitude of DigiSupport CRPL Cores is that at least between the point where the digitalis is created and the point where the CRPL interpreter checks whether the digitalis is connected, the digitalis is not registered as being connected. It would then follow that the engine only sets digitalis as being connected (for CRPL, practically speaking) the next tick. Is this expected behaviour?

I've put the code below (it's a bit long).
Spoiler
# Breeder.crpl

$breederStart:1.0
$breederMax:200.0
$breederSpeed:0.125
$digiEmit:1.1
$antibreederStart:0.0
$antibreederMax:0.0
$antibreederSpeed:0.0

once
  MapHeight 0 do
    MapWidth 0 do
      I J GetTerrain 1 eq if
        I J 2 SetCreeper
      else
        I J GetTerrain 2 eq if
          I J 1 SetCreeper
        endif
      endif
  I J GetUnitAt ->unit
  <-unit -1 neq EMITTER <-unit GetUnitType eq and if
    I J <-unit 0 "PRODUCTIONAMT" GetScriptVar SetCreeper
I J GetDigitalisGrowth if
  I J 1 SetDigitalis
endif
  endif
    loop
  loop
endonce


ElapsedTime 100 gt if
  TRUE ->hasWon
 
  MapHeight 0 do
    MapWidth 0 do
      I J GetCreeper ->creeper
      I J GetDigitalis neq0 if
    FALSE ->hasWon
    I J IsDigitalisConnected not if
  "CRPLCORE" I J CreateUnit ->unit
  <-unit "DigiSupport.crpl" AddScriptToUnit
        endif
    <-creeper 0 gt if
      I J <-digiEmit SetCreeperNoLower
    endif
  endif
      <-creeper 0 gt if FALSE ->hasWon endif
 
     
      <-creeper <-breederStart gt <-breederSpeed neq0 and if
        <-creeper <-breederSpeed add ->nextCreeper
        <-nextCreeper <-breederMax gt if
          <-breederMax ->nextCreeper
        endif
I J <-nextCreeper SetCreeperNoLower
      endif
      <-creeper neg ->anticreeper
  <-anticreeper <-antibreederStart gt if
    <-anticreeper <-antibreederSpeed add ->nextAnticreeper
    <-nextAnticreeper <-antibreederMax gt if
      <-antibreederMax ->nextAnticreeper
        endif
I J <-nextAnticreeper neg SetCreeper
  endif
    loop
  loop
 
  <-hasWon if
    0 DestroyAllEnemyUnits
Self 0 Destroy
  endif
endif

once
Self "main" "NONE" SetImage
Self CONST_DESTROYMODE 0 SetUnitAttribute
Self CONST_TAKEMAPSPACE FALSE SetUnitAttribute
Self CONST_NULLIFIERDAMAGES FALSE SetUnitAttribute
Self CONST_SNIPERTARGET FALSE SetUnitAttribute
Self CONST_BEAMTARGET FALSE SetUnitAttribute
Self CONST_COUNTSFORVICTORY TRUE SetUnitAttribute
Self CONST_CREATEPZ FALSE SetUnitAttribute
endonce
[close]

J

Don't put code tags within spoiler tags. Code tags already add a scrollbar if the code is too long. Within spoiler tags, we can only read a single line at a time. Here's the readable code (at the time of posting):

# Breeder.crpl

$breederStart:1.0
$breederMax:200.0
$breederSpeed:0.125
$digiEmit:1.1
$antibreederStart:0.0
$antibreederMax:0.0
$antibreederSpeed:0.0

once
  MapHeight 0 do
    MapWidth 0 do
      I J GetTerrain 1 eq if
        I J 2 SetCreeper
      else
        I J GetTerrain 2 eq if
          I J 1 SetCreeper
        endif
      endif
  I J GetUnitAt ->unit
  <-unit -1 neq EMITTER <-unit GetUnitType eq and if
    I J <-unit 0 "PRODUCTIONAMT" GetScriptVar SetCreeper
I J GetDigitalisGrowth if
  I J 1 SetDigitalis
endif
  endif
    loop
  loop
endonce


ElapsedTime 100 gt if
  TRUE ->hasWon
 
  MapHeight 0 do
    MapWidth 0 do
      I J GetCreeper ->creeper
      I J GetDigitalis neq0 if
    FALSE ->hasWon
    I J IsDigitalisConnected not if
  "CRPLCORE" I J CreateUnit ->unit
  <-unit "DigiSupport.crpl" AddScriptToUnit
        endif
    <-creeper 0 gt if
      I J <-digiEmit SetCreeperNoLower
    endif
  endif
      <-creeper 0 gt if FALSE ->hasWon endif
 
     
      <-creeper <-breederStart gt <-breederSpeed neq0 and if
        <-creeper <-breederSpeed add ->nextCreeper
        <-nextCreeper <-breederMax gt if
          <-breederMax ->nextCreeper
        endif
I J <-nextCreeper SetCreeperNoLower
      endif
      <-creeper neg ->anticreeper
  <-anticreeper <-antibreederStart gt if
    <-anticreeper <-antibreederSpeed add ->nextAnticreeper
    <-nextAnticreeper <-antibreederMax gt if
      <-antibreederMax ->nextAnticreeper
        endif
I J <-nextAnticreeper neg SetCreeper
  endif
    loop
  loop
 
  <-hasWon if
    0 DestroyAllEnemyUnits
Self 0 Destroy
  endif
endif

once
Self "main" "NONE" SetImage
Self CONST_DESTROYMODE 0 SetUnitAttribute
Self CONST_TAKEMAPSPACE FALSE SetUnitAttribute
Self CONST_NULLIFIERDAMAGES FALSE SetUnitAttribute
Self CONST_SNIPERTARGET FALSE SetUnitAttribute
Self CONST_BEAMTARGET FALSE SetUnitAttribute
Self CONST_COUNTSFORVICTORY TRUE SetUnitAttribute
Self CONST_CREATEPZ FALSE SetUnitAttribute
endonce

Karsten75

#2
Somebody (RB? WillBir  someone else?)  already did breeder terrain at least a year  Maybe more like 18-24 months) back in CW3 -  I think the technique was to not do it every frame and to skip some tiles.  Same way Virgil has to optimize the basic creeper and breeder sims.

ETA: Mapmaker was WillBir. 

http://knucklecracker.com/forums/index.php?topic=23092

Grabz

#3
For larger maps, we pretty much can't do anything that has to iterate over the whole map repeatedly. As Karsten75 says, you have to cut corners in terms how many cells you're going to process over an interval. I also wouldn't go over 128x128 maps.

QuoteI think the reason for the multitude of DigiSupport CRPL Cores is that at least between the point where the digitalis is created and the point where the CRPL interpreter checks whether the digitalis is connected, the digitalis is not registered as being connected. It would then follow that the engine only sets digitalis as being connected (for CRPL, practically speaking) the next tick. Is this expected behaviour?

I haven't tinkered with digitalis much but this is a plausible theory. You can probably fix it in some way by returning out of the script after the first unit is placed to let it process a frame, and then it will find the next unconnected digitalis patch on the next frame until it finds them all. However, I would advise against what you're trying to do and suggest just creating a custom unit that doesn't do anything except seed digitalis under itself. If your solution worked, the digitalis seeding unit would always be placed in the top-left most corner of the digitalis, and it would be better to let mapmakers just place it where they want the origin to be. The easiest way to seed digitalis with CRPL is to just do (CurrentCoords 1 SetDigitalis).

Also, you're using ElapsedTime incorrectly. ElapsedTime is a function that should not be used for gameplay. It is the total amount of time the application has been running for, and it is meant for code debugging to find out which parts of your code are executing slowly and bottlenecking the game. In your case, it does nothing, because it always takes a player more than 0.1 seconds to open a map from the main menu. In context of gameplay, you want to be using frames, which you can retrieve with GetGameTimeFrames. If you want to run your loop on a timer, you should use a timer:

#Run code every 10 frames (3 times a second)

GetTimer0 eq0 if
    10 SetTimer0
endif
For quicker response, reply to me directly at Grabz#4707 on Discord. Find me on the KC server: https://discord.gg/knucklecracker

Ehekatl

I'll just post my latest rendition of the breeder code, based off willbir's flip terrain. It's close to what was in #6455, but for this one, every tile will get a turn to be an emitter.

This version is meant to work with cornucanis' cwdig script so that you end up with creeper that never goes past level 6 terrain, and all terrain under it produces creeper up to (6-terrain) * creepLimit. e.g. Level 5 terrain will only have 49 creeper, level 4 terrain will have 49 * 2 = 98 creeper, and so on. I don't know if the breeder terrain in cw4 limits creeper the same way, or if it's the same limit for every terrain height.

$Interval:30
$creepAmount:1
$ACAmount:1
$creepLimit:29.9
$ACLimit:49.9
$Spacing:5
$worksOnVoid:0
$clearCreeper:0



once
MapWidth <-Spacing div ->xLoop
MapHeight <-Spacing div ->yLoop

MapWidth <-Spacing mod ->xRem
MapHeight <-Spacing mod ->yRem

0 ->xOffset
0 ->yOffset
endOnce

if(GetTimer0 eq0)
#MapHeight 0 do
# MapWidth 0 do
# I J 0 SetWall
# loop
#loop

<-xLoop 1 add 0 do
<-yLoop 1 add 0 do
J <-Spacing mul <-xOffset add ->x
I <-Spacing mul <-yOffset add ->y

#<-x <-y 1 SetWall




<-x <-y GetTerrain ->terrain
if (((<-terrain 1 lt) (<-worksOnVoid 1 eq) and)(<-terrain 1 gte) or ) #void check
<-x <-y getCreeper ->c
if (<-c 0 gt)

if (<-clearCreeper 0 eq) #breeder terrain during game
if ((<-terrain 6 lt) (<-c 6 <-terrain sub <-creepLimit mul lt) and ) #terrain < 6 and c < (6-terrain) * creepLimit
<-x <-y <-creepAmount AddCreeper
endif
else #clearing remaining creeper quickly after game is done
15 ->Interval
<-x <-y -10 SetCreeper
endif
#else if (c < 0) endif
endif
endif
loop
loop

<-xOffset 1 add ->xOffset
if (<-xOffset <-Spacing gte)
0 ->xOffset
<-yOffset 1 add ->yOffset
if (<-yOffset <-Spacing gte)
0 ->yOffset
endif
endif
<-Interval SetTimer0
endif