2d data structure without fixed boundaries

General FreeBASIC programming questions.
badidea
Posts: 2586
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: 2d data structure without fixed boundaries

Post by badidea »

grindstone wrote:I couldn't resist to write a little example program (use the arrow keys to shift the tiles on the board)
But your map is still of fixed size, right? With compiler bounds checking, I got:

Code: Select all

Aborting due to runtime error 1 (illegal function call) at line 104 of test.bas::DISPLAY()
when moving right.

Edit: I was wrong, the map is not limited, but there is a bug.
badidea
Posts: 2586
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: 2d data structure without fixed boundaries

Post by badidea »

Docicats code works perfect. A disadvantage may be that data gets copied twice on array resize.
Here used with a class to 'set' and 'get' tiles, with the map-array resized on 'set' when needed:

Code: Select all

'redimpreserve by dodicat (with some renaming & reformatting)

#macro redimpreserve(a, d1, d2) 'a = 2d-array, dimension 1 & 2
scope
	dim as integer row, col
	redim as typeof(a) copy(d1, d2)
	for row = d1
		for col = d2
			if row >= lbound(a, 1) and row <= ubound(a, 1) then
				if col >= lbound(a, 2) and col <= ubound(a, 2) then   
					copy(row, col) = a(row, col)
				end if
			end if
		next
	next
	redim a(d1, d2)
	for row = d1
		for col = d2
			a(row, col) = copy(row, col)
		next
	next
end scope
#endmacro

#macro printout(d) 'd = 2d-array
print
for row as long = lbound(d, 1) to ubound(d, 1)
	for col as long = lbound(d, 2) to ubound(d, 2)
		print d(row, col);
	next
	print
next
print       
#endmacro

'class wrapper for game

type tile_map
	dim as long cardId(any, any) 'row, col
	declare function getTile(row as long, col as long) as long
	declare sub setTile(row as long, col as long, id as long)
end type

function tile_map.getTile(row as long, col as long) as long
	if (row < lbound(cardId, 1)) or (row > ubound(cardId, 1)) then return -1
	if (col < lbound(cardId, 2)) or (col > ubound(cardId, 2)) then return -1
	return cardId(row, col)
end function

sub tile_map.setTile(row as long, col as long, id as long)
	dim as integer lbRow = lbound(cardId, 1)
	dim as integer ubRow = ubound(cardId, 1)
	dim as integer lbCol = lbound(cardId, 2)
	dim as integer ubCol = ubound(cardId, 2)
	dim as long resize = 0
	if row < lbRow then lbRow = row : resize = 1
	if row > ubRow then ubRow = row : resize = 1
	if col < lbCol then lbCol = col : resize = 1
	if col > ubCol then ubCol = col : resize = 1
	if resize = 1 then
		redimpreserve(cardId, lbRow to ubRow, lbCol to ubCol)
	end if
	cardId(row, col) = Id
end sub

'test the code, add some tiles to the map

dim as tile_map map

map.setTile(0, 0, 1) ' start tile
printout(map.cardId)

print "row bounds: " & lbound(map.cardId, 1) & " to " & ubound(map.cardId, 1)
print "col bounds: " & lbound(map.cardId, 2) & " to " & ubound(map.cardId, 2)

map.setTile(2, -5, 2) 'move down 2 rows, left 5 columns
printout(map.cardId)

print "row bounds: " & lbound(map.cardId, 1) & " to " & ubound(map.cardId, 1)
print "col bounds: " & lbound(map.cardId, 2) & " to " & ubound(map.cardId, 2)

map.setTile(3, 3, 3) 'set 3 at bottom-right, add 1 row, 3 columns
printout(map.cardId)

print "row bounds: " & lbound(map.cardId, 1) & " to " & ubound(map.cardId, 1)
print "col bounds: " & lbound(map.cardId, 2) & " to " & ubound(map.cardId, 2)

map.setTile(-2, 0, 4) 'set 4 at center-top, add 2 rows
printout(map.cardId)

print "row bounds: " & lbound(map.cardId, 1) & " to " & ubound(map.cardId, 1)
print "col bounds: " & lbound(map.cardId, 2) & " to " & ubound(map.cardId, 2)

map.setTile(-1, -2, 5) 'set 5 at some already allocated position
printout(map.cardId)

print "row bounds: " & lbound(map.cardId, 1) & " to " & ubound(map.cardId, 1)
print "col bounds: " & lbound(map.cardId, 2) & " to " & ubound(map.cardId, 2)

print
print "value of tile(0, 0): " & map.getTile(0, 0)
print "value of tile(2, -5): " & map.getTile(2, -5)
print "value of tile(3, 3): " & map.getTile(3, 3)
print "value of tile(-2, 0): " & map.getTile(-2, 0)
print "value of tile(-1, -2): " & map.getTile(-1, -2)
print "value of tile(-99, -99): " & map.getTile(-99, -99)
print "value of tile(+99, +99): " & map.getTile(+99, +99)
Another ideas that just popped up, is to work with a 'list of rows'.
So, a row added or a row increased in length when needed.
A bit like a hybrid between a flat list of tiles and a 2d-array.
Should use less memory than both and also less data is copied on resize compared to the 2d-array case.
grindstone
Posts: 862
Joined: May 05, 2015 5:35
Location: Germany

Re: 2d data structure without fixed boundaries

Post by grindstone »

badidea wrote:Edit: I was wrong, the map is not limited, but there is a bug.
Fixed.
badidea wrote:Another ideas that just popped up, is to work with a 'list of rows'.
That would mean a dynamic array of dynamic arrays (of different sizes). It's possible, but quite complicated. And I have no clue if the resizing works flawless. The advantage is that you can use negative indices.

Code: Select all

Type tTile
	number As Integer
End Type

Type tColumn
	column(any) As tTile
End Type

ReDim row(10) As tColumn 'initiate 10 rows

For x As Integer = 0 To 10 'initiate 10 columns for each row
	With row(x)
		ReDim .column(10)
	End With
Next

row(5).column(3).number = 5
? row(5).column(3).number
?
? LBound(row(5).column)
? UBound(row(5).column)
With row(5)
	ReDim Preserve .column(-5 To 11) 'resize the columns in row #5
End With
? LBound(row(5).column)
? UBound(row(5).column)
?
? row(5).column(3).number

Sleep
badidea
Posts: 2586
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: 2d data structure without fixed boundaries

Post by badidea »

grindstone wrote:And I have no clue if the resizing works flawless. The advantage is that you can use negative indices.

Code: Select all

...
With row(5)
	ReDim Preserve .column(-5 To 11) 'resize the columns in row #5
End With
Using 'redim preserve' to change the lower boundary does not work well. Everything shifts, the indexing is changed.

I made this, but I am stuck on a macro it seems. Dodicat help!
For me, using macros is like adding a whole new dimension of complexity to the already complex process of coding.

Code: Select all

#macro redimpreserve1d(_a_, _d_) '_a_ = 1d-array, _d_ = dimension
scope
	dim as integer _row_
	redim as typeof(_a_) _copy_(_d_)
	for _row_ = _d_
		if _row_ >= lbound(_a_) and _row_ <= ubound(_a_) then
			_copy_(_row_) = _a_(_row_)
		end if
	next
	redim _a_(_d_)
	for _row_ = _d_
		_a_(_row_) = _copy_(_row_)
	next
end scope
#endmacro

type row_type
	dim as long tile(any)
end type

type list_of_rows
	dim as row_type row(any) 'start with an 0-length list of pointers
	declare sub setItem(iRow as long, iCol as long, value as long)
	declare function getItem(iRow as long, iCol as long) as long
end type

'set value, expands storage if needed
sub list_of_rows.setItem(iRow as long, iCol as long, value as long)
	dim as long lbRow = lbound(row)
	dim as long ubRow = ubound(row)
	dim as long resize = 0
	if iRow < lbRow then lbRow = iRow : resize = 1
	if iRow > ubRow then ubRow = iRow : resize = 1
	if resize = 1 then
		redimpreserve1d(row, lbRow to ubRow) 'new line needed due to macro
	end if
	dim as long lbCol = lbound(row(iRow).tile)
	dim as long ubCol = ubound(row(iRow).tile)
	if iCol < lbCol then lbCol = iCol : resize = 2
	if iCol > ubCol then ubCol = iCol : resize = 2
	if resize = 2 then
		redimpreserve1d(row(iRow).tile, lbCol to ubCol) '<-- macro problem?
	end if
	row(iRow).tile(iCol) = value
end sub

function list_of_rows.getItem(row_ as long, col_ as long) as long
	return 0
end function

dim as list_of_rows rowList
rowList.setItem(0, 0, 0)
badidea
Posts: 2586
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: 2d data structure without fixed boundaries

Post by badidea »

Done. Without the macro version of 'redimpreserve1d':

Code: Select all

#define array_size(_a_) (ubound(_a_) - lbound(_a_) + 1)

'--- row_type ------------------------------------------------------------------

type row_type
	dim as long tile(any)
	declare sub resizePreserve(lb as long, ub as long)
end type

sub row_type.resizePreserve(lb as long, ub as long)
	redim as long copy(lb to ub)
	for i as long = lb to ub
		if i >= lbound(tile) and i <= ubound(tile) then
			copy(i) = tile(i)
		end if
	next
	redim tile(lb to ub)
	for i as long = lb to ub
		 tile(i) = copy(i)
	next
end sub

'--- list_of_rows --------------------------------------------------------------

type list_of_rows
	dim as row_type row(any)
	declare sub resizePreserve(lb as long, ub as long)
	declare sub setTile(iRow as long, iCol as long, value as long)
	declare function getTile(iRow as long, iCol as long) as long
	declare sub printme()
end type

sub list_of_rows.resizePreserve(lb as long, ub as long)
	redim as row_type copy(lb to ub)
	for i as long = lb to ub
		if i >= lbound(row) and i <= ubound(row) then
			copy(i) = row(i)
		end if
	next
	redim row(lb to ub)
	for i as long = lb to ub
		 row(i) = copy(i)
	next
end sub

'set value, expands storage if needed
sub list_of_rows.setTile(iRow as long, iCol as long, value as long)
	dim as long lbRow = lbound(row)
	dim as long ubRow = ubound(row)
	dim as long resize = 0
	if iRow < lbRow then lbRow = iRow : resize = 1
	if iRow > ubRow then ubRow = iRow : resize = 1
	if resize = 1 then
		resizePreserve(lbRow, ubRow)
	end if
	dim as long lbCol = lbound(row(iRow).tile)
	dim as long ubCol = ubound(row(iRow).tile)
	if iCol < lbCol then lbCol = iCol : resize = 2
	if iCol > ubCol then ubCol = iCol : resize = 2
	if resize = 2 then
		if array_size(row(iRow).tile) <= 0 then
			row(iRow).resizePreserve(iCol, iCol)
		else
			row(iRow).resizePreserve(lbCol, ubCol)
		end if
	end if
	row(iRow).tile(iCol) = value
end sub

function list_of_rows.getTile(iRow as long, iCol as long) as long
	if (iRow < lbound(row)) or (iRow > ubound(row)) then return -1
	if (iCol < lbound(row(iRow).tile)) or (iCol > ubound(row(iRow).tile)) then return -2
	return row(iRow).tile(iCol)
end function

sub list_of_rows.printme()
	dim as long minTileLb = &h7FFFFFFF, maxTileUb = &h80000000
	for iRow as long = lbound(row) to ubound(row)
		if lbound(row(iRow).tile) < minTileLb then minTileLb = lbound(row(iRow).tile)
		'if ubound(row(iRow).tile) < maxTileUb then maxTileUb = ubound(row(iRow).tile)
	next
	for iRow as long = lbound(row) to ubound(row)
		print space((lbound(row(iRow).tile) - minTileLb) - 1);
		for iTile as long = lbound(row(iRow).tile) to ubound(row(iRow).tile)
			print row(iRow).tile(iTile);
		next
		print
	next
	print "-----------------"
end sub

'--- test the code, add some tiles to the map ----------------------------------

dim as list_of_rows map

map.setTile(0, 0, 1) ' start tile
map.printme()

map.setTile(2, -5, 2) 'move down 2 rows, left 5 columns
map.printme()

map.setTile(3, 3, 3) 'set 3 at bottom-right, add 1 row, 3 columns
map.printme()

map.setTile(-2, 0, 4) 'set 4 at center-top, add 2 rows
map.printme()

map.setTile(-1, -2, 5) 'set 5 at some already allocated position
map.printme()

map.setTile(-1, +3, 6) 'set 6 on same row, further right
map.printme()

map.setTile(-1, +1, 7) 'set 7 between 5 and 6
map.printme()

'--- test the code, read the map tiles -----------------------------------------

print
print "value of tile(0, 0): " & map.getTile(0, 0)
print "value of tile(2, -5): " & map.getTile(2, -5)
print "value of tile(3, 3): " & map.getTile(3, 3)
print "value of tile(-2, 0): " & map.getTile(-2, 0)
print "value of tile(-1, -2): " & map.getTile(-1, -2)
print "value of tile(-1, +3): " & map.getTile(-1, +3)
print "value of tile(-1, +1): " & map.getTile(-1, +1)
print
print "value of tile(-99, -99): " & map.getTile(-99, 0)
print "value of tile(+99, +99): " & map.getTile(+99, 0)
print "value of tile(-99, -99): " & map.getTile(0, -99)
print "value of tile(-99, -99): " & map.getTile(0, +99)
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: 2d data structure without fixed boundaries

Post by dodicat »

In the macro version try
redim (_a_)(_d_)
(i.e. bracketed), which you have to do anyway regardless, if you have an array of arrays.
The brackets redim (_a_)(_d_) will work for simple arrays also.
Last edited by dodicat on Sep 25, 2021 11:48, edited 1 time in total.
grindstone
Posts: 862
Joined: May 05, 2015 5:35
Location: Germany

Re: 2d data structure without fixed boundaries

Post by grindstone »

IMHO the (mis-)use of an image is still the simpler solution. When using a tiles array instead of pointers (a silly idea, I confess), if there are not more than 255 different tiles, the colour depth can be reduced to 8 bits. That means a memory usage of 11kB for a 100 x 100 fields board.

Furthermore the whole board can be saved with a simple BSAVE.

Use the arrow keys for shifting, "s" for save and "l" for load:

Code: Select all

#Include Once "fbgfx.bi"
#Include Once "file.bi"

Type tTile
	kind As String
End Type

Dim As Integer x, y, display_x, display_y
Dim As String g
Dim As FB.image Ptr map, maptemp

Dim Shared As ULong mapwidth = 50, mapheight = 50
Dim Shared As UShort mapbpp = 8


Declare Sub display(viewx As ULong, viewy As ULong, map As FB.image Ptr, tile() As tTile)

'create an example array of tiles
Dim tile(8) As tTile
tile(1).kind = "village"
tile(2).kind = "town"
tile(3).kind = "forest"
tile(4).kind = "street"
tile(5).kind = "abbey"
tile(6).kind = "swamp"
tile(7).kind = "castle"
tile(8).kind = "lake"

ScreenRes 900, 900, 32

map = ImageCreate(mapwidth, mapheight, 0, mapbpp) 'create default map

'randomly place 20 tiles
Randomize
Dim As Integer tile_x, tile_y
For x As Integer = 1 To 20
	tile_x = Int(Rnd * 10)
	tile_y = Int(Rnd * 10)
	Do Until Point(tile_x, tile_y, map) = 0 'find an empty pixel
		tile_x = Int(Rnd * 10)
		tile_y = Int(Rnd * 10)
	Loop
	Dim As UByte kindOfTile = Int(Rnd * UBound(tile) + 1) 'pick a random tile
	PSet map, (tile_x, tile_y), kindOfTile 'write the array index of the tile to the map
Next

Do
	ImageInfo map, mapwidth, mapheight
	Select Case InKey
		Case Chr(255,77) 'arrow right
			If display_x >= mapwidth - 10 Then 'add one column at the right side
				maptemp = ImageCreate(mapwidth + 1, mapheight, 0, mapbpp)
				Put maptemp, (0, 0), map, (0, 0) - (mapwidth - 1, mapheight - 1), PSet
				ImageDestroy map
				map = maptemp
				maptemp = 0
			EndIf
			display_x += 1
		Case Chr(255,75) 'arrow left
			If display_x > 0 Then
				display_x -= 1
			Else 'add one column at the left side
				maptemp = ImageCreate(mapwidth + 1, mapheight, 0, mapbpp)
				Put maptemp, (1, 0), map, (0, 0) - (mapwidth - 1, mapheight - 1), PSet
				ImageDestroy map
				map = maptemp
				maptemp = 0
			EndIf
		Case Chr(255,80) 'arrow down
			If display_y >= mapheight - 10 Then 'add one row at the bottom
				maptemp = ImageCreate(mapwidth, mapheight + 1, 0, mapbpp)
				Put maptemp, (0, 0), map, (0, 0) - (mapwidth - 1, mapheight - 1), PSet
				ImageDestroy map
				map = maptemp
				maptemp = 0
			EndIf
			display_y += 1
		Case Chr(255,72) 'arrow up
			If display_y > 0 Then
				display_y -= 1
			Else 'add one row at the top
				maptemp = ImageCreate(mapwidth, mapheight + 1, 0, mapbpp)
				Put maptemp, (0, 1), map, (0, 0) - (mapwidth - 1, mapheight - 1), PSet
				ImageDestroy map
				map = maptemp
				maptemp = 0
			EndIf
		Case "s" 'save map
			BSave("gameboard.bmp", map)
		Case "l" 'load map
			If FileExists("gameboard.bmp") Then
				ImageDestroy map
				map = 0
				'get the map parameters
				Open "gameboard.bmp" For Binary As #1
				g = Input(18, #1) 'skip 18 bytes
				mapwidth = Cvi(Input(4, #1))
				mapheight = Abs(Cvi(Input(4, #1)))
				g = Input(2, #1) 'skip 2 bytes
				mapbpp = CVShort(Input(2, #1))
				Close #1
				
				map = ImageCreate(mapwidth, mapheight, 0, mapbpp) 'create map
				BLoad("gameboard.bmp", map)
				display_x = 0
				display_y = 0
			EndIf
		Case " "
			Exit Do
	End Select
	ImageInfo map, mapwidth, mapheight
	display(display_x, display_y, map, tile())
	Sleep 1
Loop

'BSave("gameboard.bmp", map)

ImageDestroy map


Sub display(viewx As ULong, viewy As ULong, map As FB.image Ptr, tile() As tTile)
	Dim As Integer x, y, scx, scy
	
	'create view section, the piece of the map that is displayed on the screen (10 x 10 tiles)
	Dim As FB.image Ptr viewsection = ImageCreate(10, 10, 0, mapbpp)
	Dim As UByte index
	Dim As String text
		
	'copy the section to be displayed to the buffer
	Get map, (viewx, viewy) - (viewx + 9, viewy + 9), viewsection
	
	ScreenLock
	Cls
	For y = 0 To 9
		For x = 0 To 9
			Line (x * 90 + 5, y * 90 + 5) - ((x + 1) * 90 - 5, (y + 1) * 90 - 5), RGB(255, 255, 255), b
			index  = Point(x, y, viewsection)
			Draw String(x * 90 + 10, y * 90 + 10), "x=" & viewx + x & " y=" & viewy + y, RGB(255, 255, 255)
			If index Then
				text = tile(index).kind
			Else
				'text = "empty"
				text = ""
			EndIf
			Draw String(x * 90 + 10, y * 90 + 30), text, RGB(255, 255, 255)
		Next
	Next
	ScreenUnLock
	
	ImageDestroy viewsection
End Sub
badidea
Posts: 2586
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: 2d data structure without fixed boundaries

Post by badidea »

dodicat wrote:In the macro version try
redim (_a_)(_d_)
(i.e. bracketed), which you have to do anyway regardless, if you have an array of arrays.
The brackets redim (_a_)(_d_) will work for simple arrays also.
Thanks, works now:

Code: Select all

#define array_size(_a_) (ubound(_a_) - lbound(_a_) + 1)

#macro redimpreserve1d(a, d) 'a = 1d-i, d = dimension
scope
	redim as typeof(a) copy(d)
	for i as integer = d
		if i >= lbound(a) and i <= ubound(a) then
			copy(i) = a(i)
		end if
	next
	redim (a)(d)
	for i as integer = d
		a(i) = copy(i)
	next
end scope
#endmacro

'--- row_type ------------------------------------------------------------------

type row_type
	dim as long tile(any)
end type

'--- list_of_rows --------------------------------------------------------------

type list_of_rows
	dim as row_type row(any) 'start with an 0-length list of pointers
	declare sub setTile(iRow as long, iCol as long, value as long)
	declare function getTile(iRow as long, iCol as long) as long
	declare sub printme()
end type

'set value, expands storage if needed
sub list_of_rows.setTile(iRow as long, iCol as long, value as long)
	dim as long lbRow = lbound(row)
	dim as long ubRow = ubound(row)
	dim as long resize = 0
	if iRow < lbRow then lbRow = iRow : resize = 1
	if iRow > ubRow then ubRow = iRow : resize = 1
	if resize = 1 then
		redimpreserve1d(row, lbRow to ubRow) 'new line needed due to macro
	end if
	dim as long lbCol = lbound(row(iRow).tile)
	dim as long ubCol = ubound(row(iRow).tile)
	if iCol < lbCol then lbCol = iCol : resize = 2
	if iCol > ubCol then ubCol = iCol : resize = 2
	if resize = 2 then
		if array_size(row(iRow).tile) <= 0 then
			redimpreserve1d(row(iRow).tile, iCol to iCol)
		else
			redimpreserve1d(row(iRow).tile, lbCol to ubCol)
		end if
	end if
	row(iRow).tile(iCol) = value
end sub

function list_of_rows.getTile(iRow as long, iCol as long) as long
	if (iRow < lbound(row)) or (iRow > ubound(row)) then return -1
	if (iCol < lbound(row(iRow).tile)) or (iCol > ubound(row(iRow).tile)) then return -2
	return row(iRow).tile(iCol)
end function

sub list_of_rows.printme()
	dim as long minTileLb = &h7FFFFFFF, maxTileUb = &h80000000
	for iRow as long = lbound(row) to ubound(row)
		if lbound(row(iRow).tile) < minTileLb then minTileLb = lbound(row(iRow).tile)
		'if ubound(row(iRow).tile) < maxTileUb then maxTileUb = ubound(row(iRow).tile)
	next
	for iRow as long = lbound(row) to ubound(row)
		print space((lbound(row(iRow).tile) - minTileLb) - 1);
		for iTile as long = lbound(row(iRow).tile) to ubound(row(iRow).tile)
			print row(iRow).tile(iTile);
		next
		print
	next
	print "-----------------"
end sub

'--- test the code, add some tiles to the map ----------------------------------

dim as list_of_rows map

map.setTile(0, 0, 1) ' start tile
map.printme()

map.setTile(2, -5, 2) 'move down 2 rows, left 5 columns
map.printme()

map.setTile(3, 3, 3) 'set 3 at bottom-right, add 1 row, 3 columns
map.printme()

map.setTile(-2, 0, 4) 'set 4 at center-top, add 2 rows
map.printme()

map.setTile(-1, -2, 5) 'set 5 at some already allocated position
map.printme()

map.setTile(-1, +3, 6) 'set 6 on same row, further right
map.printme()

map.setTile(-1, +1, 7) 'set 7 between 5 and 6
map.printme()

'--- test the code, read the map tiles -----------------------------------------

print
print "value of tile(0, 0): " & map.getTile(0, 0)
print "value of tile(2, -5): " & map.getTile(2, -5)
print "value of tile(3, 3): " & map.getTile(3, 3)
print "value of tile(-2, 0): " & map.getTile(-2, 0)
print "value of tile(-1, -2): " & map.getTile(-1, -2)
print "value of tile(-1, +3): " & map.getTile(-1, +3)
print "value of tile(-1, +1): " & map.getTile(-1, +1)
print
print "value of tile(-99, -99): " & map.getTile(-99, 0)
print "value of tile(+99, +99): " & map.getTile(+99, 0)
print "value of tile(-99, -99): " & map.getTile(0, -99)
print "value of tile(-99, -99): " & map.getTile(0, +99)
badidea
Posts: 2586
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: 2d data structure without fixed boundaries

Post by badidea »

A preview of the 'carcasonne' I am working on for which I wanted this data sctructure: https://nr100.home.xs4all.nl/badidea/ci ... -09-29.png
Currently still using dodicat's 2d-redim-preserve macro.

I'll start a new thread for this when the code is more 'mature'.
One issue is that I probably shouldn't post the tile graphics on git-hub due to copyright. I may have to draw my own tiles.
Post Reply