Letter Puzzle

Miscellaneous Forums/Blitz Showcase/Letter Puzzle

Recently someone gave me a small puzzle, consisting of 4 pieces that had to be shaped like a 'T'. It was harder then I thought, and after a long time trying it was really satisfying when I finally completed it. I've tried to make a small game out of it. It might be a bit to hard on a computer (as you can't fit the edges of the pieces together that easy), so I didn't make an option to flip the pieces (they're all flipped the correct way allready).

Download:
Intel Mac Application
BlitzMax Source Code (game uses no external media)
*You'll have to rotate (right mouse button) and move (left mouse button) the pieces so they form a capital T. You can shift trough the pieces with the keys [1-4] and [TAB].*
(I'm still looking for a way to fill the pieces with a color, only outlines atm)

I'll post the solution on request. All comments are welcome!

[EDIT]
09-10-08: the pieces are now color filled. both links are updated.

Arrgh! I can't do it (but don't post the solution yet).

evil puzzle! :)))

Here is a version with filled shapes. I have removed the tPoint structure since the polygon shapes use array data

Couple of things to fix:
1) CheckIfFinished() not implemented
2) PointInPoly() function not working yet? The shape should turn RED when the mouse is over it

The shapes x/y positional structure stays the same in this example. The movement of the poly is done by using the SetOrigin command
Only the rotation affects the array values


There is another great puzzle similar to this where you need to fill a box area with pre-defined shapes. I cannot remember the name of the puzzle though

SuperStrict

Graphics 800,600

'-----------
' SHAPE DATA
'-----------
'       num. points, x, y,   x,   y,   x,   y,   x,  y,   x,  y

Global shapes:tShape[4]
shapes[0]=tShape.Create([30.0,  -100.0,    30.0,  55.0, -40.0,  55.0, -40.0, -30.0])
shapes[1]=tShape.Create([-35.0,   -75.0,   -35.0,  25.0,  35.0,  25.0,  35.0,  -5.0])
shapes[2]=tShape.Create([-15.0,    50.0,   -15.0, -15.0,  50.0, -15.0])
shapes[3]=tShape.Create([30.0,  -100.0,    30.0,  90.0, -15.0,  45.0, -40.0, 70.0, -40.0, -30.0])


Local GameHandle:tGameHandle=tGameHandle.Create(4)

While Not KeyHit(KEY_ESCAPE)
	GameHandle.mainLoop()
	Flip
	Cls
Wend

End

'---------------------------------------------
' GameHandle type
' -Handles general flow of the game
'---------------------------------------------
Type tGameHandle
	Const SOL_DIST:Int=5 ' distance there may be between points for them to be 'connected'
	Const MODE_MOVE:Int=1
	Const MODE_ROTATE:Int=2
	Global oldGfx:Int[]=New Int[4] ' colR,colG,colB,lineW
	
	Field numShapes:Int,numSolutions:Int
	Field solData1:Int[] ' solution data [p1]
	Field solData2:Int[] ' solution data [p2]
	Field selectedShape:Int=-1
	Field currentMode:Int=MODE_MOVE
	Field tmpX:Int=-1,tmpY:Int=-1 ' mouse move

	Function Create:tGameHandle(_numshapes%)
		Local g:tGameHandle=New tGameHandle
		g.numShapes=_numshapes
		g.LoadSolutions()
		g.repositionShapes()
		g.selectedShape=0
		SetClsColor( 50,130,50 )
		Return G
	EndFunction
	
	Method loadSolutions()
		Local pX%,pY%		
		ReadData( numSolutions )
		solData1=New Int[numSolutions]
		solData2=New Int[numSolutions]
		For Local i%=0 Until numSolutions
			ReadData(pX)
			ReadData(pY)
			solData1[i]=pX
			solData2[i]=pY
		Next
	EndMethod
	
	Method repositionShapes()
		For Local i:Int=0 Until numShapes
			shapes[i].setPosition( i*100+200,300 )
		Next
	End Method
	
	Method draw()
		For Local i:Int=0 Until numShapes
			If i=selectedShape
				shapes[i].changeCol( 50,50,200 )
			Else
				shapes[i].changeCol( 255,255,255 )
			EndIf
			shapes[i].draw
		Next
		drawGUI()
	EndMethod
	
	Method drawGUI()
		setGfx( 0,0,0,2 )
		DrawRect( 0,0,GraphicsWidth(),100 )
		setGfx( 255,255,255,2 )
		DrawText( "Rotate (rmb) and move (lmb) to pieces so they form the letter:",50,10 )
		setGfx( 255,50,50,2 )
		DrawRect( 550,10,18,25 )
		setGfx( 255,255,255,2 );SetScale( 2.0,2.0 )
		DrawText( "T",550,10 );SetScale( 1.0,1.0 )
		DrawText( "Press keys [1-4] or [TAB] to select pieces",50,70 )
		restoreGfx()
	End Method
	
	Method checkIfFinsished:Byte()
		For Local i:Int=0 Until numSolutions
'			If(solData1[i]],solData2[i]) > SOL_DIST )
				Return False
'			EndIf
		Next
		Return True
	EndMethod
			
	Method mainLoop()
		handleInput()
		draw()
		If( checkIfFinsished()=True )
			completed()
		EndIf
	EndMethod
	
	Method completed()
		Local count:Int
		selectedShape=-1
		While( count < 10 )
			Delay( 80 )
			If( (count Mod 2)=0 )
				SetClsColor( 200,50,50 )
			Else
				SetClsColor( 50,200,50 )
			EndIf
			Cls
			draw()
			Flip
			count:+1
		Wend
		count=0
		setGfx( 0,0,0,2 )
		SetClsColor( 200,50,50 )
		While( count < 10 )
			Delay( 80 )
			If( (count Mod 2)=0 )
				SetClsColor( 200,50,50 )
			Else
				SetClsColor( 50,200,50 )
			EndIf
			Cls()
			draw()
			DrawRect( GraphicsWidth()/2-(count*40)/2,GraphicsHeight()/2-(count*30)/2,count*40,count*30 )
			Flip
			count:+1
		Wend
		draw()
		DrawRect( GraphicsWidth()/2-(count*40)/2,GraphicsHeight()/2-(count*30)/2,count*40,count*30 )
		setGfx( 200,200,200,2 )
		SetScale( 2.0,2.0 )
		DrawText( "Congratualations!",GraphicsWidth()/2-(count*40)/2+30,GraphicsHeight()/2-(count*30)/2+30 )
		SetScale( 2.8,4.0 )
		DrawText( "Puzzle completed!",GraphicsWidth()/2-(count*40)/2+10,GraphicsHeight()/2-(count*30)/2+100 )
		Flip
		restoreGfx()
		WaitKey()
		End
	EndMethod
	
	Method handleInput()
		If KeyDown(key_up) shapes[selectedShape].move(0,-3)
		If KeyDown(key_down) shapes[selectedShape].move(0,3)
		If KeyDown(key_left) shapes[selectedShape].move(-3,0)
		If KeyDown(key_right) shapes[selectedShape].move(3,0)
		
		If KeyHit(KEY_1) selectedShape=0
		If KeyHit(KEY_2) selectedShape=1
		If KeyHit(KEY_3) selectedShape=2
		If KeyHit(KEY_4) selectedShape=3
		If KeyHit(KEY_5) selectedShape=4
		If KeyHit(KEY_6) selectedShape=5
		If KeyHit(KEY_7) selectedShape=6
		
		If KeyDown(KEY_R) shapes[selectedShape].rotate(3)
		
		If KeyHit(KEY_TAB)
			selectedShape:+1
			If selectedShape>3 selectedShape=0
		EndIf
		
		If( MouseDown(MOUSE_LEFT) )
			If tmpX=-1 tmpX=MouseX()
			If tmpY=-1 tmpY=MouseY()
			shapes[selectedShape].move( MouseX()-tmpX,MouseY()-tmpY )
			tmpX=MouseX()
			tmpY=MouseY()	
		ElseIf( MouseDown(MOUSE_RIGHT) )	
			If tmpX=-1 tmpX=MouseX()
			If tmpY=-1 tmpY=MouseY()
			shapes[selectedShape].rotate( MouseX()-tmpX+MouseY()-tmpY )
			tmpX=MouseX()
			tmpY=MouseY()
		Else
			tmpX=-1
			tmpY=-1
		EndIf	
	EndMethod
	
	Function setGfx( _r:Int,_g:Int,_b:Int,_lw:Int )
		restoreGfx()
		GetColor( oldGfx[0],oldGfx[1],oldGfx[2] )
		SetColor( _r,_g,_b )
		SetLineWidth( _lw )
	EndFunction
	
	Function restoreGfx()
		SetColor ( tGameHandle.oldGfx[0],tGameHandle.oldGfx[1],tGameHandle.oldGfx[2] )
		SetLineWidth( oldGfx[3] )
	EndFunction
End Type

'---------------------------------------------
' Shape type
' -Holds all data for every shape
'---------------------------------------------
Type tShape
	Field posX:Int,posY:Int
	Field PolyData#[]
	Field colorR:Int=255,colorG:Int=255,colorB:Int=255
	Field drawPoints:Byte=False
	Field lineW:Int=3
	
	Function Create:tShape(pdata#[])
		Local s:tShape=New tShape
		s.PolyData=pdata
		Return s
	EndFunction
	
	Function createSquare:tShape(_w#=100.0,_h#=100.0)
		Local s:tShape=tShape.Create([0.0,0.0,_w,0.0,_w,_h,0.0,_h])
		Return s
	EndFunction
	
	Method move( _dx:Int,_dy:Int )
		posX:+_dx
		posY:+_dy
	EndMethod
	
	Method setPosition(nx#,ny# )
		posX=nx ; posY=ny
	EndMethod
	
	Method rotate( _angle:Int )
		Local xnew:Float,ynew:Float
		Local p%=0
		Repeat
			xnew=PolyData[p]*Cos(_angle)-PolyData[p+1]*Sin(_angle)
			ynew=PolyData[p]*Sin(_angle)+PolyData[p+1]*Cos(_angle)
			PolyData[p]=xnew
			PolyData[p+1]=ynew
			p:+2
		Until p>=PolyData.Length
	EndMethod
		
	Method draw()
		tGameHandle.setGfx( colorR,colorG,colorB,lineW )
		SetOrigin posX,posY
		If InsidePoly(MouseX()-posX,MouseY()-posY,polydata)
			changeCol $ff,$00,$00
		EndIf
		DrawPoly polydata
		SetOrigin 0,0
		tGameHandle.restoreGfx()
	EndMethod
	
	Method changeCol(_r:Int,_g:Int,_b:Int,_lw:Int=2)
		colorR=_r
		colorG=_g
		colorB=_b
		lineW=_lw
	EndMethod
End Type


Function calcDist:Int(s0:tShape,s1:tShape)
	Local dx:Int,dy:Int
	dx=Abs(s0.posX+s1.posX)
	dy=Abs(s0.posY+s1.posY)
	Return( Int( Sqr( dx*dx+dy*dy ) ) )
End Function

Function dot%(x0%,y0%,x1%,y1%,x2%,y2%)
	Return (x1-x0)*(y2-y1)-(x2-x1)*(y1-y0)
End Function

Function InsidePoly%(px%,py%,p#[])
	Local i%=0
	' compare x/y coords with next x/y coords in array
	Repeat
		If dot(p[i+0],p[i+1],p[i+2],p[i+3],px,py)<=0 Return False
		i:+2
	Until i=p.Length-2
	i=p.Length-2
	' compare last x/y coord pair with first
	If dot(p[i+0],p[i+1],p[0],p[1],px,py)>0 Return True
	Return False
End Function


'--------------
' SOLUTION DATA
'--------------
'    NumSolutions,   point1 id, point2 id ...
DefData 5,              3,12 , 8,11 , 10,0 , 14,7 , 4,15


The puzzle looks way better with filled shapes, just like I hoped. I'm studying your code right now, I really like the implementation of polygons. I'll try to implement CheckIfFinished() again (it's alot of work adding the solution data, I'll probably come to it tomorrow).
I could also easily add some more puzzles, as I made a basic editor that makes it easy to create shapes. If there are any suggestions, please post them.

Jim Brown, thanks for your contribution!

BTW, did anyone manage to solve the 'T' yet?

could u post a windows executable?

I currently don't have a windows computer with BlitzMax installed. If someone could compile a windows executable and send it to me, I could host it on my server.

I finished the CheckIfFinished() function and fixed some small things in Jim Browns version. I updated both the links in my first post to the new files.

Here's the code if you don't want to download it:
SuperStrict

Graphics 800,600

'-----------
' SHAPE DATA
'-----------
'       num. points, x, y,   x,   y,   x,   y,   x,  y,   x,  y

Global shapes:tShape[4]
shapes[0]=tShape.Create([30.0,  -100.0,    30.0,  55.0, -40.0,  55.0, -40.0, -30.0])
shapes[1]=tShape.Create([-35.0,   -75.0,   -35.0,  25.0,  35.0,  25.0,  35.0,  -5.0])
shapes[2]=tShape.Create([-15.0,    50.0,   -15.0, -15.0,  50.0, -15.0])
shapes[3]=tShape.Create([30.0,  -100.0,    30.0,  90.0, -15.0,  45.0, -40.0, 70.0, -40.0, -30.0])

Local GameHandle:tGameHandle=tGameHandle.Create(4)

While Not KeyHit(KEY_ESCAPE)
	GameHandle.mainLoop()
	Flip
	Cls
Wend

End

'---------------------------------------------
' GameHandle type
' -Handles general flow of the game
'---------------------------------------------
Type tGameHandle
	Const SOL_DIST:Int=8 ' distance there may be between points for them to be 'connected'
	Const MODE_MOVE:Int=1
	Const MODE_ROTATE:Int=2
	Global oldGfx:Int[]=New Int[4] ' colR,colG,colB,lineW
	
	Field numShapes:Int,numSolutions:Int
	Field solData1:Int[] ' solution data [p1]
	Field solData2:Int[] ' solution data [shape p1]
	Field solData3:Int[] ' solution data [p2]
	Field solData4:Int[] ' solution data [shape p2]
	Field selectedShape:Int=-1
	Field currentMode:Int=MODE_MOVE
	Field tmpX:Int=-1,tmpY:Int=-1 ' mouse move

	Function Create:tGameHandle(_numshapes%)
		Local g:tGameHandle=New tGameHandle
		g.numShapes=_numshapes
		g.LoadSolutions()
		g.repositionShapes()
		g.selectedShape=0
		SetClsColor( 50,130,50 )
		Return G
	EndFunction
	
	Method loadSolutions()
		Local pX%,pY%,s1%,s2%	
		ReadData( numSolutions )
		solData1=New Int[numSolutions]
		solData2=New Int[numSolutions]
		solData3=New Int[numSolutions]
		solData4=New Int[numSolutions]
		For Local i%=0 Until numSolutions
			ReadData(pX)
			ReadData(s1)
			ReadData(pY)
			ReadData(s2)
			solData1[i]=pX
			solData2[i]=s1
			solData3[i]=pY
			solData4[i]=s2
		Next
	EndMethod
	
	Method repositionShapes()
		For Local i:Int=0 Until numShapes
			shapes[i].setPosition( i*100+200,300 )
		Next
	End Method
	
	Method draw()
		For Local i:Int=0 Until numShapes
			If i=selectedShape
				shapes[i].changeCol( 150,100,100 )
			Else
				shapes[i].changeCol( 255-i*20,255-i*20,255-i*20 )
			EndIf
			shapes[i].draw
		Next
		drawGUI()
	EndMethod
	
	Method drawGUI()
		setGfx( 0,0,0,2 )
		DrawRect( 0,0,GraphicsWidth(),100 )
		setGfx( 255,255,255,2 )
		DrawText( "Rotate (rmb) and move (lmb) to pieces so they form the letter:",50,10 )
		setGfx( 255,50,50,2 )
		DrawRect( 550,10,18,25 )
		setGfx( 255,255,255,2 );SetScale( 2.0,2.0 )
		DrawText( "T",550,10 );SetScale( 1.0,1.0 )
		DrawText( "Press keys [1-4] or [TAB] to select pieces",50,70 )
		restoreGfx()
	End Method
	
	Method checkIfFinsished:Byte()
		Local i%,x1%,y1%,x2%,y2%,dx%,dy%
		For i=0 Until numSolutions
			x1=shapes[solData2[i]].PolyData[solData1[i]*2]+shapes[solData2[i]].posX
			y1=shapes[solData2[i]].PolyData[solData1[i]*2+1]+shapes[solData2[i]].posY
			x2=shapes[solData4[i]].PolyData[solData3[i]*2]+shapes[solData4[i]].posX
			y2=shapes[solData4[i]].PolyData[solData3[i]*2+1]+shapes[solData4[i]].posY
			dx=Abs(x1-x2);dy=Abs(y1-y2)
			If ( Sqr(dx*dx+dy*dy) > SOL_DIST )
				Return False
			EndIf
		Next
		Return True
	EndMethod
			
	Method mainLoop()
		handleInput()
		draw()
		If( checkIfFinsished()=True )
			completed()
		EndIf
	EndMethod
	
	Method completed()
		Local count:Int
		selectedShape=-1
		While( count < 10 )
			Delay( 80 )
			If( (count Mod 2)=0 )
				SetClsColor( 200,50,50 )
			Else
				SetClsColor( 50,200,50 )
			EndIf
			Cls
			draw()
			Flip
			count:+1
		Wend
		count=0
		setGfx( 0,0,0,2 )
		SetClsColor( 200,50,50 )
		While( count < 10 )
			Delay( 80 )
			If( (count Mod 2)=0 )
				SetClsColor( 200,50,50 )
			Else
				SetClsColor( 50,200,50 )
			EndIf
			Cls()
			draw()
			DrawRect( GraphicsWidth()/2-(count*40)/2,GraphicsHeight()/2-(count*30)/2,count*40,count*30 )
			Flip
			count:+1
		Wend
		draw()
		DrawRect( GraphicsWidth()/2-(count*40)/2,GraphicsHeight()/2-(count*30)/2,count*40,count*30 )
		setGfx( 200,200,200,2 )
		SetScale( 2.0,2.0 )
		DrawText( "Congratualations!",GraphicsWidth()/2-(count*40)/2+30,GraphicsHeight()/2-(count*30)/2+30 )
		SetScale( 2.8,4.0 )
		DrawText( "Puzzle completed!",GraphicsWidth()/2-(count*40)/2+10,GraphicsHeight()/2-(count*30)/2+100 )
		Flip
		restoreGfx()
		WaitKey()
		End
	EndMethod
	
	Method handleInput()
		If KeyDown(key_up) shapes[selectedShape].move(0,-3)
		If KeyDown(key_down) shapes[selectedShape].move(0,3)
		If KeyDown(key_left) shapes[selectedShape].move(-3,0)
		If KeyDown(key_right) shapes[selectedShape].move(3,0)
		
		If KeyHit(KEY_1) selectedShape=0
		If KeyHit(KEY_2) selectedShape=1
		If KeyHit(KEY_3) selectedShape=2
		If KeyHit(KEY_4) selectedShape=3
		If KeyHit(KEY_5) selectedShape=4
		If KeyHit(KEY_6) selectedShape=5
		If KeyHit(KEY_7) selectedShape=6
		
		If KeyDown(KEY_R) shapes[selectedShape].rotate(3)
		
		If KeyHit(KEY_TAB)
			selectedShape:+1
			If selectedShape>3 selectedShape=0
		EndIf
		
		If( MouseDown(MOUSE_LEFT) )
			If tmpX=-1 tmpX=MouseX()
			If tmpY=-1 tmpY=MouseY()
			shapes[selectedShape].move( MouseX()-tmpX,MouseY()-tmpY )
			tmpX=MouseX()
			tmpY=MouseY()	
		ElseIf( MouseDown(MOUSE_RIGHT) )	
			If tmpX=-1 tmpX=MouseX()
			If tmpY=-1 tmpY=MouseY()
			shapes[selectedShape].rotate( MouseX()-tmpX+MouseY()-tmpY )
			tmpX=MouseX()
			tmpY=MouseY()
		Else
			tmpX=-1
			tmpY=-1
		EndIf	
	EndMethod
	
	Function setGfx( _r:Int,_g:Int,_b:Int,_lw:Int )
		restoreGfx()
		GetColor( oldGfx[0],oldGfx[1],oldGfx[2] )
		SetColor( _r,_g,_b )
		SetLineWidth( _lw )
	EndFunction
	
	Function restoreGfx()
		SetColor ( tGameHandle.oldGfx[0],tGameHandle.oldGfx[1],tGameHandle.oldGfx[2] )
		SetLineWidth( oldGfx[3] )
	EndFunction
End Type

'---------------------------------------------
' Shape type
' -Holds all data for every shape
'---------------------------------------------
Type tShape
	Field posX:Int,posY:Int
	Field PolyData#[]
	Field colorR:Int=255,colorG:Int=255,colorB:Int=255
	Field drawPoints:Byte=False
	Field lineW:Int=3
	
	Function Create:tShape(pdata#[])
		Local s:tShape=New tShape
		s.PolyData=pdata
		Return s
	EndFunction
	
	Function createSquare:tShape(_w#=100.0,_h#=100.0)
		Local s:tShape=tShape.Create([0.0,0.0,_w,0.0,_w,_h,0.0,_h])
		Return s
	EndFunction
	
	Method move( _dx:Int,_dy:Int )
		posX:+_dx
		posY:+_dy
	EndMethod
	
	Method setPosition(nx#,ny# )
		posX=nx ; posY=ny
	EndMethod
	
	Method rotate( _angle:Int )
		Local xnew:Float,ynew:Float
		Local p%=0
		Repeat
			xnew=PolyData[p]*Cos(_angle)-PolyData[p+1]*Sin(_angle)
			ynew=PolyData[p]*Sin(_angle)+PolyData[p+1]*Cos(_angle)
			PolyData[p]=xnew
			PolyData[p+1]=ynew
			p:+2
		Until p>=PolyData.Length
	EndMethod
		
	Method draw()
		tGameHandle.setGfx( colorR,colorG,colorB,lineW )
		SetOrigin posX,posY
		If InsidePoly(MouseX()-posX,MouseY()-posY,polydata)
			changeCol $ff,$00,$00
		EndIf
		DrawPoly polydata
		SetOrigin 0,0
		tGameHandle.restoreGfx()
	EndMethod
	
	Method changeCol(_r:Int,_g:Int,_b:Int,_lw:Int=2)
		colorR=_r
		colorG=_g
		colorB=_b
		lineW=_lw
	EndMethod
End Type


Function calcDist:Int(s0:tShape,s1:tShape)
	Local dx:Int,dy:Int
	dx=Abs(s0.posX+s1.posX)
	dy=Abs(s0.posY+s1.posY)
	Return( Int( Sqr( dx*dx+dy*dy ) ) )
End Function

Function dot%(x0%,y0%,x1%,y1%,x2%,y2%)
	Return (x1-x0)*(y2-y1)-(x2-x1)*(y1-y0)
End Function

Function InsidePoly%(px%,py%,p#[])
	Local i%=0
	' compare x/y coords with next x/y coords in array
	Repeat
		If dot(p[i+0],p[i+1],p[i+2],p[i+3],px,py)<=0 Return False
		i:+2
	Until i=p.Length-2
	i=p.Length-2
	' compare last x/y coord pair with first
	If dot(p[i+0],p[i+1],p[0],p[1],px,py)>0 Return True
	Return False
End Function


'--------------
' SOLUTION DATA
'--------------
'    NumSolutions,   point1-id, point1-shape-id, point2-id, point2-shape-id ...
DefData 5,              3,0,1,3, 0,2,0,3, 3,3,3,1, 0,0,2,2, 0,1,4,3


I got the PointInPoly() function working now so you can directly select a shape with the mouse. Same for rotation. Just hover over a shape then RIGHT-CLICK to rotate and LEFT-CLICK to move. The shape becomes the active one under both situations

Conrad,
I had to change the 2nd shape so that it draws in a clockwise manor. The PointInPoly() function only works on polygons constructed in a clockwise fashion
Can you therefore update the puzzle completion checking routine since this is will be currently broken?

SuperStrict
Framework BRL.GLMax2D
Graphics 800,600

'-----------
' SHAPE DATA
'-----------
'       num. points, x, y,   x,   y,   x,   y,   x,  y,   x,  y

Global shapes:tShape[4]
shapes[0]=tShape.Create([ 30.0,-100.0  ,  30.0, 55.0  ,  -40.0, 55.0  ,  -40.0,-30.0])
shapes[1]=tShape.Create([ 35.0,-005.0  ,  35.0, 25.0  ,  -35.0, 25.0  ,  -35.0,-75.0  ])
shapes[2]=tShape.Create([-15.0, 050.0  , -15.0,-15.0  ,   50.0,-15.0])
shapes[3]=tShape.Create([ 30.0,-100.0  ,  30.0, 90.0  ,  -15.0, 45.0  ,  -40.0, 70.0  ,  -40.0, -30.0])

Local GameHandle:tGameHandle=tGameHandle.Create(4)

While Not KeyHit(KEY_ESCAPE)
	GameHandle.mainLoop()
	Flip
	Cls
Wend

End

'---------------------------------------------
' GameHandle type
' -Handles general flow of the game
'---------------------------------------------
Type tGameHandle
	Const SOL_DIST:Int=8 ' distance there may be between points for them to be 'connected'
	Const MODE_MOVE:Int=1
	Const MODE_ROTATE:Int=2
	Global oldGfx:Int[]=New Int[4] ' colR,colG,colB,lineW
	
	Field numShapes:Int,numSolutions:Int
	Field solData1:Int[] ' solution data [p1]
	Field solData2:Int[] ' solution data [shape p1]
	Field solData3:Int[] ' solution data [p2]
	Field solData4:Int[] ' solution data [shape p2]
	Field selectedShape:Int=-1
	Field currentMode:Int=MODE_MOVE
	Field tmpX:Int=-1,tmpY:Int=-1 ' mouse move

	Function Create:tGameHandle(_numshapes%)
		Local g:tGameHandle=New tGameHandle
		g.numShapes=_numshapes
		g.LoadSolutions()
		g.repositionShapes()
		g.selectedShape=0
		SetClsColor( 50,130,50 )
		Return G
	EndFunction
	
	Method loadSolutions()
		Local pX%,pY%,s1%,s2%	
		ReadData( numSolutions )
		solData1=New Int[numSolutions]
		solData2=New Int[numSolutions]
		solData3=New Int[numSolutions]
		solData4=New Int[numSolutions]
		For Local i%=0 Until numSolutions
			ReadData(pX)
			ReadData(s1)
			ReadData(pY)
			ReadData(s2)
			solData1[i]=pX
			solData2[i]=s1
			solData3[i]=pY
			solData4[i]=s2
		Next
	EndMethod
	
	Method repositionShapes()
		For Local i:Int=0 Until numShapes
			shapes[i].setPosition( i*100+200,300 )
		Next
	End Method
	
	Method draw()
		For Local i:Int=0 Until numShapes
			shapes[i].changeCol( 255-i*20,255-i*20,255-i*20 )
			If i=selectedShape Then shapes[i].changeCol( 80,250,60 )
			If PointInPoly(MouseX()-shapes[i].posX,MouseY()-shapes[i].posY,shapes[i].polydata)
				shapes[i].changeCol ($ff,$40,$40)
				If (MouseHit(1) Or MouseHit(2)) Then selectedShape=i
			EndIf
			shapes[i].draw
		Next
		drawGUI()
	EndMethod
	
	Method drawGUI()
		setGfx( 0,0,0,2 )
		DrawRect( 0,0,GraphicsWidth(),100 )
		setGfx( 255,255,255,2 )
		DrawText( "Rotate (rmb) and move (lmb) to pieces so they form the letter:",50,10 )
		setGfx( 255,50,50,2 )
		DrawRect( 550,10,18,25 )
		setGfx( 255,255,255,2 );SetScale( 2.0,2.0 )
		DrawText( "T",550,10 );SetScale( 1.0,1.0 )
		DrawText( "Press keys [1-4] or [TAB] to select pieces",50,70 )
		restoreGfx()
	End Method
	
	Method checkIfFinsished:Byte()
		Local i%,x1%,y1%,x2%,y2%,dx%,dy%
		For i=0 Until numSolutions
			x1=shapes[solData2[i]].PolyData[solData1[i]*2]+shapes[solData2[i]].posX
			y1=shapes[solData2[i]].PolyData[solData1[i]*2+1]+shapes[solData2[i]].posY
			x2=shapes[solData4[i]].PolyData[solData3[i]*2]+shapes[solData4[i]].posX
			y2=shapes[solData4[i]].PolyData[solData3[i]*2+1]+shapes[solData4[i]].posY
			dx=Abs(x1-x2);dy=Abs(y1-y2)
			If ( Sqr(dx*dx+dy*dy) > SOL_DIST )
				Return False
			EndIf
		Next
		Return True
	EndMethod
			
	Method mainLoop()
		handleInput()
		draw()
		If( checkIfFinsished()=True )
			completed()
		EndIf
	EndMethod
	
	Method completed()
		Local count:Int
		selectedShape=-1
		While( count < 10 )
			Delay( 80 )
			If( (count Mod 2)=0 )
				SetClsColor( 200,50,50 )
			Else
				SetClsColor( 50,200,50 )
			EndIf
			Cls
			draw()
			Flip
			count:+1
		Wend
		count=0
		setGfx( 0,0,0,2 )
		SetClsColor( 200,50,50 )
		While( count < 10 )
			Delay( 80 )
			If( (count Mod 2)=0 )
				SetClsColor( 200,50,50 )
			Else
				SetClsColor( 50,200,50 )
			EndIf
			Cls()
			draw()
			DrawRect( GraphicsWidth()/2-(count*40)/2,GraphicsHeight()/2-(count*30)/2,count*40,count*30 )
			Flip
			count:+1
		Wend
		draw()
		DrawRect( GraphicsWidth()/2-(count*40)/2,GraphicsHeight()/2-(count*30)/2,count*40,count*30 )
		setGfx( 200,200,200,2 )
		SetScale( 2.0,2.0 )
		DrawText( "Congratualations!",GraphicsWidth()/2-(count*40)/2+30,GraphicsHeight()/2-(count*30)/2+30 )
		SetScale( 2.8,4.0 )
		DrawText( "Puzzle completed!",GraphicsWidth()/2-(count*40)/2+10,GraphicsHeight()/2-(count*30)/2+100 )
		Flip
		restoreGfx()
		WaitKey()
		End
	EndMethod
	
	Method handleInput()
		If KeyDown(key_up) shapes[selectedShape].move(0,-3)
		If KeyDown(key_down) shapes[selectedShape].move(0,3)
		If KeyDown(key_left) shapes[selectedShape].move(-3,0)
		If KeyDown(key_right) shapes[selectedShape].move(3,0)
		
		If KeyHit(KEY_1) selectedShape=0
		If KeyHit(KEY_2) selectedShape=1
		If KeyHit(KEY_3) selectedShape=2
		If KeyHit(KEY_4) selectedShape=3
		If KeyHit(KEY_5) selectedShape=4
		If KeyHit(KEY_6) selectedShape=5
		If KeyHit(KEY_7) selectedShape=6
		
		If KeyDown(KEY_R) shapes[selectedShape].rotate(3)
		
		If KeyHit(KEY_TAB)
			selectedShape:+1
			If selectedShape>3 selectedShape=0
		EndIf
		
		If( MouseDown(MOUSE_LEFT) )
			If tmpX=-1 tmpX=MouseX()
			If tmpY=-1 tmpY=MouseY()
			shapes[selectedShape].move( MouseX()-tmpX,MouseY()-tmpY )
			tmpX=MouseX()
			tmpY=MouseY()	
		ElseIf( MouseDown(MOUSE_RIGHT) )	
			If tmpX=-1 tmpX=MouseX()
			If tmpY=-1 tmpY=MouseY()
			shapes[selectedShape].rotate( MouseX()-tmpX+MouseY()-tmpY )
			tmpX=MouseX()
			tmpY=MouseY()
		Else
			tmpX=-1
			tmpY=-1
		EndIf	
	EndMethod
	
	Function setGfx( _r:Int,_g:Int,_b:Int,_lw:Int )
		restoreGfx()
		GetColor( oldGfx[0],oldGfx[1],oldGfx[2] )
		SetColor( _r,_g,_b )
		SetLineWidth( _lw )
	EndFunction
	
	Function restoreGfx()
		SetColor ( tGameHandle.oldGfx[0],tGameHandle.oldGfx[1],tGameHandle.oldGfx[2] )
		SetLineWidth( oldGfx[3] )
	EndFunction
End Type

'---------------------------------------------
' Shape type
' -Holds all data for every shape
'---------------------------------------------
Type tShape
	Field posX:Int,posY:Int
	Field PolyData#[]
	Field colorR:Int=255,colorG:Int=255,colorB:Int=255
	Field drawPoints:Byte=False
	Field lineW:Int=3
	
	Function Create:tShape(pdata#[])
		Local s:tShape=New tShape
		s.PolyData=pdata
		Return s
	EndFunction
	
	Function createSquare:tShape(_w#=100.0,_h#=100.0)
		Local s:tShape=tShape.Create([0.0,0.0,_w,0.0,_w,_h,0.0,_h])
		Return s
	EndFunction
	
	Method move( _dx:Int,_dy:Int )
		posX:+_dx
		posY:+_dy
	EndMethod
	
	Method setPosition(nx#,ny# )
		posX=nx ; posY=ny
	EndMethod
	
	Method rotate( _angle:Int )
		Local xnew:Float,ynew:Float
		Local p%=0
		Repeat
			xnew=PolyData[p]*Cos(_angle)-PolyData[p+1]*Sin(_angle)
			ynew=PolyData[p]*Sin(_angle)+PolyData[p+1]*Cos(_angle)
			PolyData[p]=xnew
			PolyData[p+1]=ynew
			p:+2
		Until p>=PolyData.Length
	EndMethod
		
	Method draw()
		tGameHandle.setGfx( colorR,colorG,colorB,lineW )
		SetOrigin posX,posY
		DrawPoly polydata
		SetOrigin 0,0
		tGameHandle.restoreGfx()
	EndMethod
	
	Method changeCol(_r:Int,_g:Int,_b:Int,_lw:Int=2)
		colorR=_r
		colorG=_g
		colorB=_b
		lineW=_lw
	EndMethod
End Type


Function calcDist:Int(s0:tShape,s1:tShape)
	Local dx:Int,dy:Int
	dx=Abs(s0.posX+s1.posX)
	dy=Abs(s0.posY+s1.posY)
	Return( Int( Sqr( dx*dx+dy*dy ) ) )
End Function

Function dot%(x0%,y0%,x1%,y1%,x2%,y2%)
	Return (x1-x0)*(y2-y1)-(x2-x1)*(y1-y0)
End Function

Function PointInPoly%(px%,py%,p#[])
	Local i%=0
	' compare x/y coords with next x/y coords in array
	Repeat
		If dot(p[i+0],p[i+1],p[i+2],p[i+3],px,py)<=0 Return False
		i:+2
	Until i=p.Length-2
	i=p.Length-2
	' compare last x/y coord pair with first
	If dot(p[i+0],p[i+1],p[0],p[1],px,py)>0 Return True
	Return False
End Function


'--------------
' SOLUTION DATA
'--------------
'    NumSolutions,   point1-id, point1-shape-id, point2-id, point2-shape-id ...
DefData 5,            3,0,1,3,   0,2,0,3, 3,3,3,1,   0,0,2,2,   0,1,4,3