Code in Development: Creating Carcassone in CRPL

Started by Flabort, January 20, 2014, 09:13:49 PM

Previous topic - Next topic

Flabort

I really enjoy the work I am doing in CRPL right now. The flip emitters were an awesome idea, and I like seeing people use them in their maps, interpreting them how they want to; The battle arena was fun, and I enjoyed the randomly genned space islands with flip emitters. Coming up with an algorithm to cycle the colors of creeper was a tough challenge, but isn't in a map yet (and for good reason, it's very laggy and and hard on the eyes). Marrying the Flip Emitters to Slip Emitters was fun, too, and I put a custom texture on that world for fun.

But it's time for the ultimate challenge, implementing a board game. This one, Carcassonne.

I will be making it with 2 CPU opponents, and no multiplayer support (though I have ideas... I can't actually do those). I will not include any expansions, though I have lots of them to get pieces from if I wanted to.

I am starting this thread to chronicle my scripts, and ask for advice when I need it. Wish me luck! :D
My maps: Top scores: Sugarplum, Cryz Dal, Cryz Torri, Cryz Bohz (Click fetch scores, page courtesy of kwinse)

Flabort

#1
First: I need to figure out how to handle legal positions for mouseclicks, and I think I have a solution:
Spoiler
0 GetMouseButtonDown if
GetMouseCell  3 FALSE GetAllUnitsInRange ->Numunits ->TempList
<-Numunits 0 do
1 ->onbutton
<-TempList I GetListElement Self eq if
break
endif
0 ->onbutton
loop
<-onbutton if
@Mousclick
endif
endif

:Mouseclick
[close]
By placing a core with a script like this, I think I can reliably determine if it was clicked.

Next, the size of the pieces. I determine that I'll need to be able to place a meeple on the feature, so each needs to be minimum 3 cells wide. Not all cities reach the nearest road, even if it goes right across, so the tile has to be able to handle that. So let's make it 7 "features" across/tall. And then each feature is minimum 3... but the click range is 3, so let's make each one 5 cells, except between the road and city where it's 3; resulting in a 31x31 cell tile. So we have 15 on either side of the center. -15 to 15. From there I can make a list of coords and heights for each tile. Easy enough so far.

Edit: Actually, let's go smaller. 31*31 is a big list. 961 length. Obviously I must pick a more sane size. How about 3+2+3+2+3? That's 11x11, a lot easier :P
Edit 2: Math fail. However, 3+1+3+1+3 does work quite well.
Edit 3:
:Tile2E
ClearStack CreateList
1 1 1 1 2 2 2 1 1 1 1
4 1 1 1 2 2 2 1 1 1 1
4 1 1 1 2 2 2 1 1 1 1
4 4 1 1 2 2 2 1 1 1 1
4 4 4 1 2 2 2 2 2 2 2
4 4 4 1 2 2 2 2 2 2 2
4 4 4 1 2 2 2 2 2 2 2
4 4 1 1 2 2 2 1 1 1 1
4 1 1 1 2 2 2 1 1 1 1
4 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
AppendStackToList ->TileList

:Tile2N
ClearStacok CreateList
1 4 4 4 4 4 4 4 4 4 1
1 1 1 4 4 4 4 4 1 1 1
1 1 1 1 4 4 4 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
AppendStackToList ->TileList

There's a space on each field, city, and/or road segment for a meeple. Yay! I'll have to lower the range of the click finder, though, for placing meeples.
Edit4: Added this to each tile so far:
CreateList 0 4 0 -3 3 AppendStackToList ->ValidX
CreateList -4 3 0 3 3 AppendStackToList ->ValidY

Except with more or less coord pairs depending on the number of valid positions. This one applies to Tile2N. Each pair is in the center of a 3x3 or larger segment, with only one per valid segment. So while there are 2 3x3 areas on the field segment next to the city, only one of them is a valid coord pair.
My maps: Top scores: Sugarplum, Cryz Dal, Cryz Torri, Cryz Bohz (Click fetch scores, page courtesy of kwinse)

Clean0nion

I don't care what you do with this, but I am going to play the damn out of it.
Spoiler
I once tried to remake Catan. Not a good idea.
[close]

Flabort

So, the order of a turn:
1) Draw the piece. A piece is selected, figured out if it has been drawn yet, and an image (not terrain) of the piece is shown.
2) Select orientation. The image is displayed 3 more times, in different orientations.  The script figures out if that orientation is legal in any position; if it's not legal, it is not shown.
3) Select placement. Legal positions for placing the tile at this orientation are shown. Clicking one hides the others, and then the void is turned to terrain using a nested do loop and the list of heights.
4) Meeple placement. A skip button is shown, as well as valid positions on the piece, found using my last code, and compared to the feature to find if anything else is already on that feature; if there is, then it's validity is negated. If it's still valid, and there are less than 7 GREEN meeples on the field, then it displays a clickable symbol.
5) Scoring. If the piece completed a feature, check for a meeple (Green, red, or black), then if there is, score it depending on what it is (height 1: field, not scored till end of game; height 2: road; height 3: cloister; height 4: city; height 5: not really a feature.)
6) Black's turn; does the same thing, with less clickable spots and more internal thinking.
7) Red's turn; ditto.
8) return to 1) unless there are no more pieces
9) Score fields and incomplete features

I think I'll use a single parent core, which creates the clickable cores; they return a variable of their ID and Coords to the parent, who then destroys all currently clickable cores and takes action. There will also be invisible cores in the center of each tile, for the purposes of identifying the tiles for cloister scoring and legal tile placement.

Also, I was considering Catan instead, but don't know it well enough.  ;) At least after set-up, you'd have a static board, even if you can't handle hexes. Hint:
Spoiler
[close]
My maps: Top scores: Sugarplum, Cryz Dal, Cryz Torri, Cryz Bohz (Click fetch scores, page courtesy of kwinse)

Clean0nion

I am so desperate to make one of my board games. But first I need to finish SW3, Eiqualagua, and Sall System.
Then I've got a list of the games I have.
Spoiler
Funkenschlag (Power Grid)
Settlers of Catan + 2 expansions
Smallworld
Ticket To Ride
Alhambra (which is quite similar to Carcassone)
Village
Thurn und Taxis (Thurn and Taxis in English)
Kingdom Builder
Dixit
And of course, Monopoly and Cluedo.
[close]

Village would be too difficult to, well, explain, in CW3; Funkenschlag really does require another player; even trying to recreate all the different scoring methods in Kingdom Builder would take aeons; Thurn und Taxis would take far too long as well; Dixit requires creativity (not to mention very specific images and possibly inappropriate images [not sexual, just disturbing really]) that an AI would be unable to provide...
Alhambra seems like my best bet. And no matter how complex Smallworld is, it is by far my favourite and I am going to remake it at some point.

Flabort

Cluedo! Do Cluedo! :P JK, choose whichever you feel is best; I always had a difficulty time understanding Cluedo.

So, as to my idea concerning multiplayer: A display of 9-25 small squares in the corner of the screen on screen-mode. A script keeps track of several keypresses on the keyboard... but none of those keys are on most or any keyboards. Stuff like "☼" or "♣". The game won't start without one of these, and warns you that you need to run "CW3_something_MP.exe"; starting this outside script and then connecting to another player causes the script to "press" these non-existant keys, feeding them into the in-game scripts. The game in turn updates the colorful squares, which feed the outside script. The script could also act as a Computer player in it's own right, instead of connecting to a player.

However, I'm not doing that, because I don't know C++ yet.




I will use a While loop for each phase of the turn; the computer turns are included in this. After all the while loops all complete, the script naturally restarts and goes back to the top, where the first phase (drawing of tile from the 'bag') starts again. The pieces are included in function calls in the main script, as already seen.

Say, does "[255,0,0]" create a list, or do you still require the Create List function? I feel good with what I have, but that's idle curiosity that could be used to change the color of your meeples/opponent's meeples, if I wanted to implement that.
My maps: Top scores: Sugarplum, Cryz Dal, Cryz Torri, Cryz Bohz (Click fetch scores, page courtesy of kwinse)

Flabort

Uh-oh. Ran into my first problem where I should ask for help. Is there a @! equivalent function? (Where ->! and <-! take a string and use it as a variable, so @! would use a string as a function call)

If not, what is the fastest way to pick one of 19*4=76 functions similarly named? If it's a bunch of if's, then I guess I'll make that another function, feed in a number, and be able to eliminate some redundant functions; For example, :Tile4 instead of :Tile4E, :Tile4N, :Tile4W, and :Tile4S; or :Tile3E and :Tile3N with no :Tile3S or :Tile3W.
My maps: Top scores: Sugarplum, Cryz Dal, Cryz Torri, Cryz Bohz (Click fetch scores, page courtesy of kwinse)

Michionlion

#7
Parameters are your friends, you should not be making 76 different functions similarly named, if you are then there is probably a better and simpler way to do it.  Try finding the things in common, and the things different about each method, and create a couple that take parameters that define exactly what they need to do.
"Remember kids, the only difference between science and messing around is writing it down."
                                                                                                                         - Adam Savage

My website
My CW1, and CW2 maps!

Flabort

#8
Yeah, I guess I'll just store 76 lists into separate variables instead of the same variable, cut out the easy functions, and shove them all into one function only called in the once block.
Sorry if that sounds sarcastic, it half is half isn't; I'm not sure if I should actually do that, or if I should stay the course.

Edit: I need to store the terrain map for each tile; since tiles can vary so much while staying the same, I can't exactly cut each tile into 4 pieces; or even 9 pieces. I can store the edge-data seperately and use just that for most calls, but when placing the piece I need the SetTerrainHeight data, and that involves a list for every orientation of every piece.
My maps: Top scores: Sugarplum, Cryz Dal, Cryz Torri, Cryz Bohz (Click fetch scores, page courtesy of kwinse)

mykael

I don't know CPRL, but are you able to access elements in a list (or words/characters/substrings in a string) other than the first one?

If your data is always stored in the same cell sequence for each tile then, given the first character in a tile, you should be able to simply pull out the values you need for each side as they'll be at a fixed offset.

If your tile is:


1  1  2
2  2  3
3  2  4


Then the north edge would be characters 1, 2 and 3, the west edge would be characters 1, 4, and 7 etc...

If they are all stored as a long string '1 1 2 2 2 3 3 2 4 2 2 4 5 5 3 2 3 1' then the tiles would be 0 indexed off of 9 x the tile count (because in my example each tile has 9 height values).  So the top of the second tile in the string would be 9 + 0, 9 + 1, 9 + 2 which are '2 2 4'.

eduran

Quote from: Flabort on January 21, 2014, 08:02:40 PM
Say, does "[255,0,0]" create a list, or do you still require the Create List function? I feel good with what I have, but that's idle curiosity that could be used to change the color of your meeples/opponent's meeples, if I wanted to implement that.
CRPL does not understand [ and ], the compiler will give you an error if you try that.

Quote from: Flabort on January 21, 2014, 09:45:26 PM
Uh-oh. Ran into my first problem where I should ask for help. Is there a @! equivalent function? (Where ->! and <-! take a string and use it as a variable, so @! would use a string as a function call)

If not, what is the fastest way to pick one of 19*4=76 functions similarly named? If it's a bunch of if's, then I guess I'll make that another function, feed in a number, and be able to eliminate some redundant functions; For example, :Tile4 instead of :Tile4E, :Tile4N, :Tile4W, and :Tile4S; or :Tile3E and :Tile3N with no :Tile3S or :Tile3W.
If you want to stay close to your current format you could use something like this:
@Tile('4N')
:Tile
->tileID
if(<-tileID eq("4N"))
# tile4N
ClearStack CreateList ->TileList
1 1 1 1 2 2 2 1 1 1 1
4 1 1 1 2 2 2 1 1 1 1
4 1 1 1 2 2 2 1 1 1 1
4 4 1 1 2 2 2 1 1 1 1
4 4 4 1 2 2 2 2 2 2 2
4 4 4 1 2 2 2 2 2 2 2
4 4 4 1 2 2 2 2 2 2 2
4 4 1 1 2 2 2 1 1 1 1
4 1 1 1 2 2 2 1 1 1 1
4 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
AppendStackToList(<-TileList)
else if
....


Quote from: Flabort on January 22, 2014, 12:09:13 AM
and that involves a list for every orientation of every piece.
You could store only one orientation and rotate the height data as neccessary.

Flabort

Quote from: eduran on January 22, 2014, 05:31:49 AM
If you want to stay close to your current format you could use something like this:
snip
That's roughly what I was thinking of when I said a "bunch of if's"; I wouldn't have used the warp or else, but it's the same idea.
Quote from: eduran on January 22, 2014, 05:31:49 AM
Quote from: Flabort on January 22, 2014, 12:09:13 AM
and that involves a list for every orientation of every piece.
You could store only one orientation and rotate the height data as neccessary.
Brilliant. That cuts down from 79 back to 19, which really cuts down my workload, as well as redundancy. It would work on the valid meeple locations, too. I'd just need to work "from the top right going down then left" instead of "from the top left going right then down". Etc for the two other rotations.

Quote from: mykael on January 22, 2014, 03:26:49 AM
I don't know CPRL, but are you able to access elements in a list (or words/characters/substrings in a string) other than the first one?
I'm not entirely sure what the rest of your suggestion means, but yes for lists. Not sure about inside a string, but you can use "GetListElement" to get any element from a list; I'm using it with "I" and "K" in a "do" loop to get the data out in sequence; the list doesn't store the linebreaks in my example (and eduran's), so it's just one long list of 121 numbers. By turning 11 of those numbers at a time into terrain, then doing the same one row down or one column over, I can theoretically get the outcome to look like the example.
Quote from: mykael on January 22, 2014, 03:26:49 AMIf your data is always stored in the same cell sequence for each tile then, given the first character in a tile, you should be able to simply pull out the values you need for each side as they'll be at a fixed offset.
But sides overlap, that's why I argued against that; If I place a city side, then an overlapping road side, it might look like this:
11112221111
11112221111
11112221111
11112221111
4441
4441
4441
4411
4111
4111
5111

which would break the city side. Notice that much of the city is missing. And then there's this piece (#19):
51112221111
41112221111
41112221111
44112222111
44511222222
44441222222
44441112222
44444411111
44444451111
44444444111
44444444445

Could you make that out of pieces of pieces #1 and #2? I mean, the big corner-to-corner city piece is in #1, and #2 has a T road, but this road is curved; and the middle section of the piece would be even wonkier to make part of such an algorithm that cuts the tiles apart.
My maps: Top scores: Sugarplum, Cryz Dal, Cryz Torri, Cryz Bohz (Click fetch scores, page courtesy of kwinse)

Flabort

My main loop so far; keep in mind that anything involving clicking or display will involve another script, and this is very incomplete.
Spoiler
once
0 ->piecedrawn
0 ->drawnPiece
CreateList 1 1 1 1 1 2 2 2 3 3 3 4 4 4 4 5 5 6 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8 9 9 9 9 10 11 11 11 11 12 12 12 13 13 14 14 14 14 14 15 15 15 16 16 16 17 17 17 18 18 18 19 19 19 19 19 AppendStackToList ->PieceDefine
71 0 do
1 "pieceinbag" I concat ->!
<-PieceDefine I GetListElement "pieceshape" I concat ->!
loop
ElaspedTime asint dup ->randSeed ->nextTime
CreateList ->AllTilesPlaced
endonce

@drawpiece
@orientpiece
@placepiece
@meeple
@score
"Red" ->ComPlayer
@CPU
"Black" ->ComPlayer
@CPU

:awake
TRUE EnableAlternateControlMode

:drawpiece
while <-piecedrawn 0 neq repeat
RanPut 71 mul ->drawnPiece
#Here I check if the game's just starting, and get the starting piece.
<-AllTilesPlaced GetListCount eq0 if
55 ->drawnPiece
endif
"pieceinbag" <-drawnPiece concat <-! if
"pieceshape" <-drawnPiece concat <-!
#Need to put here the code for finding valid locations; if there is none, do nothing?
#Need to put here the code for picking the image to put up
0 "pieceinbag" <-drawnPiece concat ->!
else
#Redraw code goes here; put piece back and take it again.
endif
endwhile

:FindTile ->Tempvar ->Orientation
<-Tempvar 1 eq if
#Need to put here the code for determining which function to call

:RanPut
<-randSeed ElaspedTime asint add <-nextTime sub
ElaspedTime asint ->nextTime
RandInt RandInt sub add
mul (16807) mod (2147483647) ->randSeed
<-randSeed abs 2147483647.0 div

:TileCreate
->tileID

<-tileID 1 eq if
ClearStack Create List
5 1 1 1 1 1 1 1 1 1 1
4 1 1 1 1 1 1 1 1 1 1
4 1 1 1 1 1 1 1 1 1 1
4 4 1 1 1 1 1 1 1 1 1
4 4 5 1 1 1 1 1 1 1 1
4 4 4 4 1 1 1 1 1 1 1
4 4 4 4 1 1 1 1 1 1 1
4 4 4 4 4 4 1 1 1 1 1
4 4 4 4 4 4 5 1 1 1 1
4 4 4 4 4 4 4 4 1 1 1
4 4 4 4 4 4 4 4 4 4 5
AppendStackToList ->TileList
CreateList -3 0 AppendStackToList ->ValidX
CreateList 1 0 AppendStackToList ->ValidY
endif

<-tileID 2 eq if
ClearStack CreateList
5 1 1 1 2 2 2 1 1 1 1
4 1 1 1 2 2 2 1 1 1 1
4 1 1 1 2 2 2 1 1 1 1
4 4 1 1 2 2 2 1 1 1 1
4 4 4 1 2 2 2 2 2 2 2
4 4 4 1 2 2 2 2 2 2 2
4 4 4 1 2 2 2 2 2 2 2
4 4 1 1 2 2 2 1 1 1 1
4 1 1 1 2 2 2 1 1 1 1
4 1 1 1 2 2 2 1 1 1 1
5 1 1 1 2 2 2 1 1 1 1
AppendStackToList ->TileList
CreateList -4 -3 0 3 3 AppendStackToList ->ValidX
CreateList 0 -4 0 3 -3 AppendStackToList ->ValidY
endif

<-tileID 3 eq if
ClearStack Create List
5 1 1 1 1 1 1 1 1 1 5
4 1 1 1 1 1 1 1 1 1 4
4 1 1 1 1 1 1 1 1 1 4
4 4 1 1 1 1 1 1 1 4 4
4 4 4 1 1 1 1 1 4 4 4
4 4 4 1 1 1 1 1 4 4 4
4 4 4 1 1 1 1 1 4 4 4
4 4 1 1 1 1 1 1 1 4 4
4 1 1 1 1 1 1 1 1 1 4
4 1 1 1 1 1 1 1 1 1 4
5 1 1 1 1 1 1 1 1 1 5
AppendStackToList ->TileList
CreateList 0 -4 4 AppendStackToList ->ValidX
CreateList 0 0 0 AppendStackToList ->ValidY
endif

<-tileID 4 eq if
ClearStack Create List
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 5 5 3 3 3 1 1 1
1 1 1 5 5 3 3 3 1 1 1
1 1 1 3 3 3 3 3 1 1 1
1 1 1 3 3 3 3 3 1 1 1
1 1 1 3 3 3 3 3 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
AppendStackToList ->TileList
CreateList 1 0 AppendStackToList ->ValidX
CreateList 1 4 AppendStackToList ->ValidY
endif

<-tileID 5 eq if
ClearStack Create List
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 5 5 3 3 3 1 1 1
1 1 1 5 5 3 3 3 2 2 2
1 1 1 3 3 3 3 3 2 2 2
1 1 1 3 3 3 3 3 2 2 2
1 1 1 3 3 3 3 3 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
AppendStackToList ->TileList
CreateList 1 0 0 AppendStackToList ->ValidX
CreateList 1 4 -4 AppendStackToList ->ValidY
endif

<-tileID 6 eq if
ClearStack Create List
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
AppendStackToList ->TileList
CreateList 0 3 3 -3 -3 AppendStackToList ->ValidX
CreateList 0 3 -3 3 -3 AppendStackToList ->ValidY
endif

<-tileID 7 eq if
ClearStack Create List
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 2 1 1 1
1 1 1 1 1 2 2 2 2 2 2
1 1 1 1 1 2 2 2 2 2 2
1 1 1 1 1 1 1 2 2 2 2
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
AppendStackToList ->TileList
CreateList 1 4 0 AppendStackToList ->ValidX
CreateList -1 -3 -2  AppendStackToList ->ValidY
endif

<-tileID 8 eq if
ClearStack Create List
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
AppendStackToList ->TileList
CreateList 0 0 0 AppendStackToList ->ValidX
CreateList 0 -3 3 AppendStackToList ->ValidY
endif

<-tileID 9 eq if
ClearStack Create List
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 2 2 2 2
1 1 1 1 2 2 2 2 2 2 2
1 1 1 1 2 2 2 2 2 2 2
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
1 1 1 1 2 2 2 1 1 1 1
AppendStackToList ->TileList
CreateList 0 -3 3 3 AppendStackToList ->ValidX
CreateList 0 0 3 -3 AppendStackToList ->ValidY
endif

<-tileID 10 eq if
ClearStack Create List
4 4 4 4 4 4 4 4 4 4 4
4 4 4 4 4 4 4 4 4 4 4
4 4 4 4 4 4 4 4 4 4 4
4 4 4 4 4 4 4 4 4 4 4
4 4 4 4 4 4 4 4 4 4 4
4 4 4 4 4 4 4 4 4 4 4
4 4 4 4 4 4 4 4 4 4 4
4 4 4 4 4 4 4 4 4 4 4
4 4 4 4 4 4 4 4 4 4 4
4 4 4 4 4 4 4 4 4 4 4
4 4 4 4 4 4 4 4 4 4 4
AppendStackToList ->TileList
CreateList 0 AppendStackToList ->ValidX
CreateList 0 AppendStackToList ->ValidY
endif

<-tileID 11 eq if
ClearStack Create List
4 4 4 4 4 4 4 4 4 4 4
4 4 4 4 4 4 4 4 4 4 1
4 4 4 4 4 4 4 4 4 5 1
4 4 4 4 4 4 4 4 4 1 1
4 4 4 4 4 4 4 4 1 1 1
4 4 4 4 4 4 4 4 1 1 1
4 4 4 4 4 4 4 4 1 1 1
4 4 4 4 4 4 4 4 4 1 1
4 4 4 4 4 4 4 4 4 5 1
4 4 4 4 4 4 4 4 4 4 1
4 4 4 4 4 4 4 4 4 4 4
AppendStackToList ->TileList
CreateList 0 4 AppendStackToList ->ValidX
CreateList 0 0 AppendStackToList ->ValidY
endif

<-tileID 12 eq if
ClearStack Create List

AppendStackToList ->TileList
CreateList AppendStackToList ->ValidX
CreateList AppendStackToList ->ValidY
endif

<-tileID 13 eq if
ClearStack Create List

AppendStackToList ->TileList
CreateList AppendStackToList ->ValidX
CreateList AppendStackToList ->ValidY
endif

<-tileID 14 eq if
ClearStack Create List

AppendStackToList ->TileList
CreateList AppendStackToList ->ValidX
CreateList AppendStackToList ->ValidY
endif

<-tileID 15 eq if
ClearStack Create List

AppendStackToList ->TileList
CreateList AppendStackToList ->ValidX
CreateList AppendStackToList ->ValidY
endif

<-tileID 16 eq if
ClearStack Create List

AppendStackToList ->TileList
CreateList AppendStackToList ->ValidX
CreateList AppendStackToList ->ValidY
endif

<-tileID 17 eq if
ClearStack Create List

AppendStackToList ->TileList
CreateList AppendStackToList ->ValidX
CreateList AppendStackToList ->ValidY
endif

<-tileID 18 eq if
ClearStack Create List

AppendStackToList ->TileList
CreateList AppendStackToList ->ValidX
CreateList AppendStackToList ->ValidY
endif

<-tileID 19 eq if
ClearStack Create List
5 1 1 1 2 2 2 1 1 1 1
4 1 1 1 2 2 2 1 1 1 1
4 1 1 1 2 2 2 1 1 1 1
4 4 1 1 2 2 2 2 1 1 1
4 4 5 1 1 2 2 2 2 2 2
4 4 4 4 1 2 2 2 2 2 2
4 4 4 4 1 1 1 2 2 2 2
4 4 4 4 4 4 1 1 1 1 1
4 4 4 4 4 4 5 1 1 1 1
4 4 4 4 4 4 4 4 1 1 1
4 4 4 4 4 4 4 4 4 4 5
AppendStackToList ->TileList
CreateList -3 -3 1 4 AppendStackToList ->ValidX
CreateList 1 -4 -1 -3 AppendStackToList ->ValidY
endif
[close]
My maps: Top scores: Sugarplum, Cryz Dal, Cryz Torri, Cryz Bohz (Click fetch scores, page courtesy of kwinse)

mykael

I'd be making #19 out of #1 and #7. 

Some of the system I've seen have a set of tiles that make the terrain and a second set that they overlay to make roads and the like.  Thought about encoding your roads with a 0 where you want the terrain to show through and a 2 where you want road?  Might work for cities as well.

So take the mountain corner tile and overlay it with the road corner tile and you might get something like tile 19.  Each terrain tile would probably need a list of the road tiles that are eligible to overlay it - along with their valid orientations.

Flabort

A function that I included in the OnceBlock:
:InitMeeples
CreateList ->PlayerMeeples
CreateList ->CPU1Meeples
CreateList ->CPU2Meeples
3 0 do 7 0 do
"CRPLCORE" J I CreateUnit
J 0 eq if
<-PlayerMeeples swap AppendToList
<-PlayerMeeples I GetListElement "Meeple.crpl" AddScriptToUnit
<-PlayerMeeples I GetListElement "Meeple.crpl" "Player" 0 SetScriptVar
<-PlayerMeeples I GetListElement "Meeple.crpl" "Red" 0 SetScriptVar
<-PlayerMeeples I GetListElement "Meeple.crpl" "Blue" 63 SetScriptVar
<-PlayerMeeples I GetListElement "Meeple.crpl" "Green" 255 SetScriptVar
endif
J 1 eq if
<-CPU1Meeples swap AppendToList
<-CPU1Meeples I GetListElement "Meeple.crpl" AddScriptToUnit
<-CPU1Meeples I GetListElement "Meeple.crpl" "Player" 1 SetScriptVar
<-CPU1Meeples I GetListElement "Meeple.crpl" "Red" 255 SetScriptVar
<-CPU1Meeples I GetListElement "Meeple.crpl" "Blue" 0 SetScriptVar
<-CPU1Meeples I GetListElement "Meeple.crpl" "Green" 63 SetScriptVar
endif
J 2 eq if
<-CPU2Meeples swap AppendToList
<-CPU2Meeples I GetListElement "Meeple.crpl" AddScriptToUnit
<-CPU2Meeples I GetListElement "Meeple.crpl" "Player" 2 SetScriptVar
<-CPU2Meeples I GetListElement "Meeple.crpl" "Red" 31 SetScriptVar
<-CPU2Meeples I GetListElement "Meeple.crpl" "Blue" 31 SetScriptVar
<-CPU2Meeples I GetListElement "Meeple.crpl" "Green" 31 SetScriptVar
endif
loop loop

Creates 7 meeples per player, sets the color of each, and puts them into a list of them. Now I can work on the Meeple script; however, next comes the scripting for determining what edges are legal to go against what edges.
My maps: Top scores: Sugarplum, Cryz Dal, Cryz Torri, Cryz Bohz (Click fetch scores, page courtesy of kwinse)