Knuckle Cracker

Creeper World 3 => The Coder's Corner => Topic started by: Karsten75 on December 17, 2012, 11:21:05 AM

Title: Analysis of a CRPL script
Post by: Karsten75 on December 17, 2012, 11:21:05 AM
I'm working my way through Example 9 and it's complicated, so I thought I'd do it here and you guys can either follow along or correct me.

Here is the script:
# Create a wandering emitter.  
# The emitter leaves a Creeper trail as it moves around.
# Whenever it changes directions, it leaves a pool of creeper.
# When destroyed, it emits a bunch of Creeper and fires spores at player units.

# Setup an initial movement.  
# This is so we don't leave a pool
# of Creeper on the first movement.
once
  RandCoords swap 2 div swap 1 QueueMove
endonce

# If the current movement queue is empty, pick a new spot to move to.
GetQueuedMoveCount eq0 if
  # We will move randomly around on the left side of the map.
  # So we pick Random Coordinates, but then divide
  # the X coordinate by 2 so it is on the left side of the map.
  RandCoords swap 2 div swap 1 QueueMove
 
  # Leave a pool of Creeper since we are changing
  #directions and moving to a new spot.
  CurrentCoords 100 AddCreeper
endif

# We want to leave a trail of Creeper behind.  So we emit 2 Creeper every 10 frames
GetTimer0 eq0 if
  CurrentCoords 2 AddCreeper
  10 SetTimer0
endif

# When Destroyed, emit a bunch of Creeper and choose 5 units and fire spores at them.
:destroyed
CurrentCoords 1000 AddCreeper
5 0 do
  CurrentCoords RandUnitCoords 1 20 CreateSpore
loop


What I'm not sure about is the order things end up on the stack.  (edit:)

According to the Forth Emulator, 50 60 2 div will yield

50 30 on the stack.

So the Stack is LIFO (Last IN, First Out).

In this table I'm going to try and represent the stack:
































Executed:Stack contentsStack Diagram (http://en.wikipedia.org/wiki/Stack-oriented_programming_language)Comment
once--
RandCoords60, 50 -- 60 50Assume X=60, Y=50
Swap50, 6060 50 -- 50 60
2 Div 50 60, 2 50 60 -- 50 60 2 -- 50 30Divide X-Coordinate by 2
swap30 5050 30 -- 30 50
1 QueueMove25, 30, 130 50 1 --X, Y,  Speed, for queued move
endonceStack will now be empty, since Queuemove consumed three values from the stack.
GetQueuedMoveCount1 -- 1Number of queued moves
eq00 1 -- 0 There was one queued move, so EQ0 returns False (0)
if0 0 -- 0 Since we already stacked a move, this is 0 (false) again and we won't execute this on the first pass.
RandCoords swap 2 div swap 1 QueueMoveSame as code bracketed by [once]
CurrentCoords45 55-- 45 55X and Y coordinate of tower.Let's assume at X=45, Y=55
10045, 55 100 45 55 -- 45 55 100
AddCreeper45 55 100 --Stack's empty again
GetTimer0Value of timer0 -- 0Should be zero on first pass
EQ010 -- 1Assuming timer was 0, then this returns True (1)
if1 --True
Currentcoords -- 44 40It's moving towards 50 30, X=44, y=40
244, 40 2 44 40 -- 44 40 2
Addcreeper44 40 2 --Add the creeper, stack's empty
10 settimer0 -- Pushed value 10, settimer0 consumed the value 10 as timing interval
Destroyed:
CurrentCoords 1000 AddCreeper-- destroyed at X=56, Y=28, 1000 units
5 0 do 5 0 -- Loop 5 times starting at 0
CurrentCoords-- 56 28
RandUnitCoords 56 28 -- 56 28 12 32Unit (at (Eg.) X=12, Y=32
1 20 56 28 12 32 -- 56 28 12 32 1 20
CreateSpore56 28 12 32 1 20  -- Stack's empty, spore on its way!

Edit: I reworked this a bit. Hopefully tomorrow I'll get some time to test it. :)
Title: Re: Analysis of a CRPL script
Post by: Karsten75 on December 17, 2012, 12:02:40 PM
I am using, awkwardly, this Forth emulator (http://forthfreak.net/jsforth80x25.html) to see what the results should be.
Title: Re: Analysis of a CRPL script
Post by: knucracker on December 17, 2012, 12:54:33 PM
The order of x and y are flipped in the first few lines of your assessment.  It's not big deal other than in your description.  The end result should be that the x coordinate is divided by two, not the y coordinate.

Many commands require an x and a y coordinate.  These commands expect the y coordinate to be higher on the stack than the x, or right most when reading from left to right.   So:

# X Y Amt SetCreeper
RandX RandY 1 SetCreeper


This can be simplified to:

RandCoords 1 SetCreeper


RandCoords just pushes two values to the stack, X first, then Y second.  So, U is on top of the stack.

Another way to visualize the stack is exactly like a physical stack of items.  Imagine a stack of plates or trays at a cafeteria.  You push plates down onto the stack one at a time.  If you take one, you take the one off the top of the stack.  So this is a LIFO structure.

You can also use stack effect diagrams to show stack changes ( http://en.wikipedia.org/wiki/Stack-oriented_programming_language ).  In these diagrams the right most elements indicates the top of the stack and the "--" separates the before and after stack views.

Another convenience when reading any RPL is to look for the action verbs, the read backwards left from there. 

1 2 3 4 5 add sub mul div


rewrite the 'verbs' or actions on different lines as the following.  I show the stack effect diagram to the right of each line. 

1 2 3 4 5    -- 1 2 3 4 5
add           1 2 3 4 5 -- 1 2 3 9
sub           1 2 3 9 -- 1 2 -6
mul           1 2 -6 -- 1 -12   
div            1 -12 -- 0


Note that 1 divided by -12 will yield 0 since these are all integers.  If I had pushed "1.0 2.0 3.0 4.0 5.0" to the stack, the the division would have used floating point arithmetic and the final entry on the stack would have been -0.08333333333.


Title: Re: Analysis of a CRPL script
Post by: Karsten75 on December 17, 2012, 01:05:51 PM
Quote from: virgilw on December 17, 2012, 12:54:33 PM
The order of x and y are flipped in the first few lines of your assessment.  It's not big deal other than in your description.  The end result should be that the x coordinate is divided by two, not the y coordinate.

That *is* a big deal, It means I don't at all understand it yet. :(
Title: Re: Analysis of a CRPL script
Post by: knucracker on December 17, 2012, 02:07:05 PM
RandCoords pushes two values to the stack, an X and a Y coordinate.  They are pushed in that order, so Y is at the top of the stack and X is under it.  It's the same as calling
RandX and RandY in that order.

The stack change diagram would be: [ -- X Y ].
This means nothing is on the stack, and the result is X and Y on the stack with Y being on the top (the right most elements represent the top most elements in the stack).

This is how it looks arranged vertically, with Y on top of the stack.

Top
___
| Y |
| X |
---

Bottom


So, on your first line in your analysis your comment should read: "Assume X=50, Y=60"

You clearly understand stack manipulation in your analysis.  The only thing you got wrong was the order that RandCoords pushes values to the stack.  I could have done them in either order when I implemented the guts of that command (it's arbitrary), but I chose to put them in the current order so that they read X and Y from left to right.
Title: Re: Analysis of a CRPL script
Post by: Karsten75 on December 17, 2012, 02:13:25 PM
You made me LOL, I should change the code, not the comment! :P
Title: Re: Analysis of a CRPL script
Post by: knucracker on December 17, 2012, 02:26:55 PM
Instead of this:
RandCoords swap 2 div swap 1 QueueMove

You could do this:
RandX 2 div RandY 1 QueueMove

I guess this is a case where using the individual Rand functions is cleaner than using the monolithic RandCoords function.
Title: Re: Analysis of a CRPL script
Post by: Karsten75 on December 17, 2012, 02:37:44 PM
OK, I *think* I fixed the comment and stack appearance in the "once" section of my analysis. If you agree (there was also another error) then I'll move on a bit later. Right now I need a tea break.
Title: Re: Analysis of a CRPL script
Post by: lurkily on December 17, 2012, 07:17:37 PM
Okay.  I'm trying to make a link between two positions that transports creeper instantly.  Basically, it averages the two locations, and sets both locations to the average.

I'm fumbling here.  First, I built this.

CurrentCoords GetCreeper 55 31 GetCreeper add 2 div This should get the average.  Right?

To parse that a little, (((CurrentCoords GetCreeper) (55 31 GetCreeper) add) 2 div).  I have a gut feeling that I'm handling this the wrong way.

Next . . . I need to set creeper at TWO locations to be the average of both.  But the moment I set creeper in one location, the average changes.Will I need to offset the cell that transfers creeper from the tower by one cell?  Or am I thinking about this the wrong way?

EDIT: Needs to be capable of a two-way flow.
Title: Re: Analysis of a CRPL script
Post by: knucracker on December 17, 2012, 07:34:21 PM
That should calculate the average Creeper in the two cells you show (current and 55,31).  So that is correct... what isn't correct is that I am internally rounding down the Creeper to the nearest int.  I used to return Creeper as an integer, but switched that to floats yesterday... but forgot to change the return type on one function.  I'll correct this in about 15 minutes and post an updated build.

To see what is going on you can do this:


ShowTraceLog
ClearTraceLog
CurrentCoords GetCreeper Trace


Trace pops and item from the stack and puts it in a Trace log.  The ShowTraceLog command pops up a little window on the unit so you can view the log.  Calling this repeatedly isn't harmful.  The ClearTraceLog call clears the log.  This keeps it from just filling up with the same value over and over in this example.
Title: Re: Analysis of a CRPL script
Post by: lurkily on December 17, 2012, 07:36:34 PM
THAT'S what the trace log is . . . here I was thinking I'd have to dig up a text file or something.

EDIT: Any advice on the above issue?  It's the heisenberg uncertainty principle at work in code.
Title: Re: Analysis of a CRPL script
Post by: Grauniad on December 17, 2012, 07:38:21 PM
For unit movement, what is the speed? cells per frame?

If a unit has a move order, does it do nothing else during that period of time?

Let's say I want to move a unit 50 squares and drop 10 creeper and a digitalis network as it moves....
Title: Re: Analysis of a CRPL script
Post by: knucracker on December 17, 2012, 07:41:54 PM
Quote from: lurkily on December 17, 2012, 07:36:34 PM
THAT'S what the trace log is . . . here I was thinking I'd have to dig up a text file or something.

EDIT: Any advice on the above issue?  It's the heisenberg uncertainty principle at work in code.

I'm not quite following you on the second part of your problem....

Note that a script executes atomically, if that helps at all.  So if you do something to different cells in the same script invocation, they happen in sequence in the script, but nothing else happens in the game while this script is running.  Each script is part of each CRPLTower's update that happens once per frame.
Title: Re: Analysis of a CRPL script
Post by: knucracker on December 17, 2012, 07:50:45 PM
Quote from: Grauniad on December 17, 2012, 07:38:21 PM
For unit movement, what is the speed? cells per frame?

If a unit has a move order, does it do nothing else during that period of time?

Let's say I want to move a unit 50 squares and drop 10 creeper and a digitalis network as it moves....

Speed is in pixels per frame, where a cell is 8x8 pixels.  You can use floating point values (like 0.1 for a very slow mover).

Queued movements happen automatically outside of each script.  Think of them like movement instructions you have given to the CRPLTower.  It will follow those instructions independently of what your script continues to do.  So you can do whatever you like in the script while the unit is moving, including issuing a Delay command, etc.

Take this example

once
  10 10 1 QueueMove
  50 10 1 QueueMove
  50 50 1 QueueMove
  10 50 1 QueueMove
  10 10 1 QueueMove
endonce

CurrentCoords 2 SetCreeper



This queues up 4 movement orders once when the unit starts.  They will move it in a square and that's it.  No more movement after these complete.  While these movements are taking place creeper gets dropped every frame.

Moving and dropping creeper and digitalis would be very similar to Ex 10 in the docs post.  Instead of picking random coordinates, you could toggle between two locations, or three, etc.
Title: Re: Analysis of a CRPL script
Post by: lurkily on December 17, 2012, 08:03:45 PM
I need to set two different cells to the average of their own level of creeper.  So a bare cell of creeper and a cell at depth five should instantly change to both being at 2.5.  

But I was worried that once I used SetCreeper, the result of the average would also change.  I needed to set creeper in the same cells that I was averaging.  

It looks like it isn't an issue.  I have it set to do the calculations once a second, tracing each time I do the average, after setting creeper and I haven't noticed any mismatches as the integer turns over.

It looks like the 'SetCreeper' doesn't actually change the value until after the script is done running.

Either that, or creeper values are just not granular enough until the updated build is out to show a mismatch.

Anyway, I'm using this.  Much code is mirrored in the comments with me using parenthesis to help me parse what is doing what. #Set creeper in position 1 to the average of Position 1 + 2
#(55 35 (((CurrentCoords GetCreeper) (55 31 GetCreeper) add) 2 div) SetCreeper)
55 35 CurrentCoords GetCreeper 55 31 GetCreeper add 2 div SetCreeper
# Set Creep in position 2 to the average of position 1 and 2
#(CurrentCoords (((CurrentCoords GetCreeper) (55 31 GetCreeper) add) 2 div) SetCreeper)
CurrentCoords CurrentCoords GetCreeper 55 31 GetCreeper add 2 div SetCreeper
30 Delay
EDIT: Using a shorter delay causes tons of creeper to just be 'lost' when it's too thin, as it divides already-thin creeper by two, and evaporates it.
Title: Re: Analysis of a CRPL script
Post by: lurkily on December 17, 2012, 08:17:14 PM
And . . . . I'm not sure what's going on.  The cells that transfer creeper seem to absorb HUGE amounts, but seem to output very little.  I'm going to put this away, and come back to it in the morning.
Title: Re: Analysis of a CRPL script
Post by: knucracker on December 17, 2012, 08:18:38 PM
The new build is up with a fixed GetCreeper...

What you are doing should be a problem.  SetCreeper is "live" meaning it should change the contents of the cell immediately upon execution.  What you really need to do is to calculate the average once, before you do any SetCreeper call.  Then use that value for each call.

This teeters (actually, it goes over) the line for needing something else for variable management.  I have left out any heap calls, or registers, since I wanted to see how long it would be before someone hit the need.  To make this work right now, you have to do this.  Hold on, it's ugly....  (I can just hear Grauniad laughing maniacally after reading the following):

CurrentCoords GetCreeper 55 31 GetCreeper add 2 div
dup
55 swap 35 swap SetCreeper
CurrentX swap CurrentY swap SetCreeper


Title: Re: Analysis of a CRPL script
Post by: knucracker on December 17, 2012, 08:22:09 PM
Also, I understand the math of what you are trying to do (set two cells to the average value of the two cells), but I'm not grasping why you are wanting to do this.  Are you trying to make sure two pools stay at the same level (over time)?
Title: Re: Analysis of a CRPL script
Post by: lurkily on December 17, 2012, 08:34:57 PM
Wow.  That verges on a hack.  That's the problem I expected, pretty much. 

Basically, I'm experimenting with creating what is basically a micro-rift that passes Creeper/AC.

Title: Re: Analysis of a CRPL script
Post by: knucracker on December 17, 2012, 08:45:33 PM
Ahhhhh....
Yes, you face 'the problem'.  If the microrift flows in both directions, it gets a little complicated.  The average might work... will have to think about it.  A one way rift is easier, btw, and you could create two towers that each teleport only one way.  In that case, they only teleport if the destination is less than the current location.

The newly posted build seems to work fine with the above code I posted.  That code just calculates the average once, duplicates it (since it will be needed twice) then swaps it into position so it is the correct argument for each SetCreeper call.

What would have made this much cleaner was local variable support.  I just haven't decided on the exact syntax for that and if I want it to be a free form variable name or more like a register.  For instance

CurrentCoords GetCreeper 55 31 GetCreeper add 2 div Write0
55 35 Read0 SetCreeper
CurrentCoords Read0 SetCreeper

This is one super simple way to support local variables.  There are just 8 or so Read/Write functions and you can use them to store and recall things for the current script invocation.

The other way is to allow some arbitrary string to follow Read (separated by a space or an underscore).  That would be the variable name and you could have many of them....
Title: Re: Analysis of a CRPL script
Post by: lurkily on December 17, 2012, 08:51:10 PM
Yes, one-way is infinitely easier.

Updated my build.

It doesn't behave as expected for me.  I put in a delay of 60 on the script, too.  It DOES transit creeper, yes.  But I've got an emitter cells away, depth of 50, delay of 1 frame.  EDIT:Trapped on a small island with that emitter, too.

One cell away from the CRPL tower, depth is always 15 or 17.  ON the tower, it's hovering around  4.15.  At the destination, 1.15.

Somehow, it's digging a hole in 50-depth creeper, all the way down to 4-5, and only outputting miniscule amounts.
Title: Re: Analysis of a CRPL script
Post by: knucracker on December 17, 2012, 11:18:43 PM
Let me see your code, or a save game, and I'll take a look.  The average technique seems to work for me as a bidirectional gateway.

Another technique would be to subtract the source and the destination creeper values and then take some fraction of that difference and subtract it from one cell and add it to another.  That would work the same way as the Creeper sim works internally.  It would basically link two remote cells as if they were next to each other.  Creeper would always flow from the greatest to the least, but take some time to do so.
Title: Re: Analysis of a CRPL script
Post by: lurkily on December 18, 2012, 08:33:02 AM
I may have found the problem.  The output log shows build 83, not 83a.  I may have left the same .zip file open, and when I opened the new file, and had two windows open on that far monitor, I may have re-installed the old build.  I'll add to this post when I try re-installing.


That seems to have corrected it.  Another problem is that I was looking so hard to understand your dups and swaps that I forgot to check the numbers.  Creeper would transmit to 55,35, but the average function was a copy of the first one I posted, which was 55,31, and I didn't catch that and clean it up.

So it was averaging cells in two places, but the transit terminus was slightly offset.  I still think the flaw in SetCreeper was responsible, though.

Eventually, I plan to design an entire region that conducts creeper fast much like digitalis - but instantly.  That way each cell of the region takes the average value, for very fast flanking moves.  Evap will limit spread to some degree, so digitalis would be needed to limit that.

That will probably need to wait for variable support, though, or else things are going to get very complicated.  (I personally wouldn't need named variables, though it makes some things less complex.)

After that all we need is a way to construct fields, and attach them to these towers.

EDIT: Sorry to have you here analyzing this script when your time's much better-occupied working on the game. 
Title: Re: Analysis of a CRPL script
Post by: knucracker on December 18, 2012, 09:40:55 AM
Don't apologize... this sort of thing is why I posted the build and it's what I'm looking for.  Watching what people do with CRPL (if anything) is very important input in determining whether I keep it or not as player exposed functionality. 

CRPL represents a huge degree of power in the hands of a maps maker.  With that power they could make a truly beautiful experience or a horrible nightmare.  What I don't want is 10000 tricksters all making joke maps.  If the complexity of the language is high enough to keep away the casual idiot, but accessible enough to let through an interested player... then I might thread the needle and keep it.
Title: Re: Analysis of a CRPL script
Post by: teknotiss on December 18, 2012, 11:13:39 AM
Quote from: virgilw on December 18, 2012, 09:40:55 AM
Don't apologize... this sort of thing is why I posted the build and it's what I'm looking for.  Watching what people do with CRPL (if anything) is very important input in determining whether I keep it or not as player exposed functionality. 

CRPL represents a huge degree of power in the hands of a maps maker.  With that power they could make a truly beautiful experience or a horrible nightmare.  What I don't want is 10000 tricksters all making joke maps.  If the complexity of the language is high enough to keep away the casual idiot, but accessible enough to let through an interested player... then I might thread the needle and keep it.
just in my own experience of code monkeys, some of the nastiest tricksters have been expert coders. i spent a lot of my college career fixing the hacks/deletions (of non-computer students course work) from the jokers/evil-fiends in my class.
i've barely started looking at the possibilties of CRPL and i can see that you could end up with lots of barely winnable maps and nearly unkillable bosses. but given the scope of the code there isn't much option for limiting that behaviour, except through map mods i suppose.
Title: Re: Analysis of a CRPL script
Post by: TrickyDragon on December 18, 2012, 12:31:19 PM
in terms of these towers, they can be OP, but i think we would do better to put the restraints on maps through modding rather than the limit of the unit function.   There will be bad maps, and there will be great ones......