89 lines
3 KiB
Text
89 lines
3 KiB
Text
// 15 Puzzle // 26 september 2023 //
|
|
|
|
begin globals
|
|
CFMutableStringRef board : board = fn MutableStringNew
|
|
end globals
|
|
|
|
|
|
void local fn buildUI
|
|
Long i, j, k = 1 // k is button number
|
|
window 1, @"15 Puzzle", ( 0, 0, 200, 200 ), 3
|
|
for j = 3 to 0 step -1 : for i = 0 to 3 // Top to bottom, left to right
|
|
button k, Yes, 1, @"", ( 20 + 40 * i, 20 + 40 * j , 40, 40 ), , NSBezelStyleShadowlessSquare
|
|
ControlSetFont(k, fn FontSystemFontOfSize( 21 ) )
|
|
ControlSetAlignment( k, NSTextAlignmentCenter )
|
|
k ++
|
|
next : next
|
|
menu 1, , 1, @"File": menu 1, 1, , @"Close", @"w" : MenuItemSetAction( 1, 1, @"performClose:" )
|
|
editmenu 2 : menu 2, 0, No : menu 3, , , @"Level"
|
|
for i = 1 to 8 : menu 3, i, , str( i ) : next
|
|
MenuSetOneItemOnState( 3, 3 )
|
|
end fn
|
|
|
|
|
|
void local fn newGame
|
|
CFStringRef s
|
|
Long i, m, n = 16, p = 0 // n is empty starting tile, p holds previous move
|
|
Bool ok
|
|
MutableStringSetString (board, @" 123456789ABCDEF " )
|
|
for i = 1 to fn MenuSelectedItem( 3 )^2 // Number of shuffles is square of level
|
|
do : ok = Yes
|
|
m = n + int( 2.6 * rnd( 4 ) - 6.5 ) // Choose a random move, but
|
|
if m < 1 or m > 16 or m == p then ok = No // not of bounds or previous,
|
|
if n mod 4 = 0 and m = n + 1 then ok = No // and don't exchange eg tile 4 and 5
|
|
if n mod 4 = 1 and m = n - 1 then ok = No // or 9 and 8
|
|
until ok = Yes // Found a move, swap in board string
|
|
s = mid( board, m, 1 ) : mid( board, m, 1 ) = @" " : mid( board, n, 1 ) = s
|
|
p = n : n = m
|
|
next
|
|
for i = 1 to 16 // Stamp the buttons, p is unicode of board char, s is button title
|
|
p = (Long) fn StringCharacterAtIndex( board, i )
|
|
if p > 64 then s = fn StringWithFormat ( @"%d", p - 55 ) else s = mid( board, i, 1 )
|
|
button i, Yes, 1, s
|
|
if fn StringIsEqual( s, @" ") == Yes then button i, No
|
|
next
|
|
end fn
|
|
|
|
|
|
void local fn move ( n as Long )
|
|
CFStringRef s
|
|
Long i, m, x = -1 // x is empty plot
|
|
Bool ok
|
|
for i = 1 to 4 // see if clicked button is next to empty plot
|
|
m = n + int( 2.6 * i - 6.5 ) // -4. -1, +1, +4
|
|
ok = Yes
|
|
if m < 1 or m > 16 then ok = No // Not out of bounds etc
|
|
if n mod 4 = 0 and m = n + 1 then ok = No
|
|
if n mod 4 = 1 and m = n - 1 then ok = No
|
|
if ok == Yes
|
|
if fn StringIsEqual( mid( board, m, 1 ), @" " ) then x = m
|
|
end if
|
|
next
|
|
if x > -1 // Swap places in board string and button titles
|
|
s = mid( board, n, 1 ) : mid( board, n, 1 ) = @" " : mid( board, x, 1 ) = s
|
|
button x, Yes, 1 , fn ButtonTitle( n ) : button n, No, 1, @" "
|
|
end if
|
|
if fn StringIsEqual( board, @" 123456789ABCDEF " )
|
|
alert 112, , @"Well done.", @"Another game?", @"Yes;No", Yes
|
|
end if
|
|
end fn
|
|
|
|
|
|
void local fn doMenu( mnu as Long, itm as Long )
|
|
if mnu == 3 then MenuSetOneItemOnState( 3, itm ) : fn newGame
|
|
end fn
|
|
|
|
|
|
void local fn DoDialog( evt as Long, tag as Long )
|
|
select evt
|
|
case _btnClick : fn move( tag )
|
|
case _alertDidEnd : if tag == NSAlertFirstButtonReturn then fn newGame else end
|
|
end select
|
|
end fn
|
|
|
|
fn buildUI
|
|
fn newGame
|
|
|
|
on dialog fn doDialog
|
|
on menu fn doMenu
|
|
handleevents
|