Skip to content

Instantly share code, notes, and snippets.

@scottwalters
Last active January 16, 2019 19:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save scottwalters/56d270bbdf0d7560644a6e0ff209810a to your computer and use it in GitHub Desktop.
Save scottwalters/56d270bbdf0d7560644a6e0ff209810a to your computer and use it in GitHub Desktop.
* snobol4 -d 100M scab-client.sno
-include 'random.sno'
-include 'http.sno'
-include 'host.sno'
srandomdev()
dictfile = 'twl06.txt' ;* whole thing
* dictfile = 'twl06.txt.test' ;* part way through a
* dictfile = 'twl06.txt.test2' ;* through e
nums = '-0123456789'
data('tile.new(tile.letter,tile.id)')
rack_x = 1620
rack_y = 200
* sort letters of word
define('sortw(str)a,i,j') :(sortw_end)
sortw a = array(size(str))
sw1 i = i + 1; str len(1) . a<i> = :s(sw1)
a = sort(a)
sw2 j = j + 1; sortw = sortw a<j> :s(sw2)f(return)
sortw_end
* subpermutations
* find anagrams and sub-anagrams of str
define('subpermutations(str)strlen,sw,i,s1,s2,pat,res,res_i,anagrams,word') :(subpermutations_end)
subpermutations
* collect() ;* just gives an error about blowing the stack
* str = dict<str> ;* can't do this because a random jumble of rack letters probably won't be in the dictionary
str = sortw(str)
res = array('0:100')
i = 0 ;* index in to ana_dict, looking for matches
res_i = 0 ;* index in to res, recording results
pat = pos(0) ;* pattern gets built as a function of letters in the sorted word we're looking searching for matches for
subpermutations_1
str len(1) . s1 rem . str :f(subpermutations_2) ;* pick out the next letter from our sorted rack tiles
pat = pat ( s1 | '' ) :(subpermutations_1) ;* add a pattern to either match it or not
subpermutations_2
pat = pat pos( *strlen ) ;* force it to match through to the end. the * defers evaluation of strlen and we update this value each loop iteration. XXX probably a better way to match the end of the string.
subpermutations_3
i = i + 1
sw = ana_dict<i> :f(subpermutations_last)
strlen = size(sw)
sw pat :f(subpermutations_3)
anagrams = ana< sw > ;* add all of the anagrams to res
subpermutations_4
anagrams span(&lcase) . word span(' ') = '' :f(subpermutations_3)
res< res_i = res_i + 1 > = word :(subpermutations_4)
subpermutations_last
res<0> = res_i ;* record the index of the last match
subpermutations = res :(return)
subpermutations_end
* subpermutations2
* find anagrams and sub-anagrams of str that must also match another pattern
* match founds words, prefixed with prefix, against pat2, before including a result
* only returns up to 20 results
define('subpermutations2(str,prefix,pat2,line,exact_size)strlen,sw,i,s1,s2,pat,res,res_i,anagrams,word') :(subpermutations2_end)
subpermutations2
* collect() ;* just gives an error about blowing the stack
str = sortw(str)
res = array('0:20')
i = 0 ;* index in to ana_dict, looking for matches
res_i = 0 ;* index in to res, recording results
pat = pos(0) ;* pattern gets built as a function of letters in the sorted word we're looking searching for matches for
subpermutations2_1
str len(1) . s1 rem . str :f(subpermutations2_2) ;* pick out the next letter from our sorted rack tiles
pat = pat ( s1 | '' ) :(subpermutations2_1) ;* add a pattern to either match it or not
subpermutations2_2
pat = pat pos( *strlen ) ;* force it to match through to the end. the * defers evaluation of strlen and we update this value each loop iteration. XXX probably a better way to match the end of the string.
subpermutations2_3
i = i + 1
sw = ana_dict<i> :f(subpermutations2_last)
strlen = size(sw)
eq(strlen, exact_size) :f(subpermutations2_3)
sw pat :f(subpermutations2_3)
anagrams = ana< sw > ;* add all of the anagrams to res
subpermutations2_4
anagrams ( span(&lcase) . word span(' ') ) = '' :f(subpermutations2_3)
output = 'XXX subpermutations2 trying to match >' prefix word '< against extra restriction pattern which looks like >' line '<'
* complicated and not working for some reason now
(prefix word '**************' ) pat2 :f(subpermutations2_4) ;* if user extra user supplied pat doesn't match the word prefixed with prefix, don't return it
res< res_i = res_i + 1 > = word :(subpermutations2_4)
subpermutations2_last
res<0> = res_i ;* record the index of the last match
subpermutations2 = res :(return)
subpermutations2_end
* http_get() modified from HTTP.OPEN() to add user headers
define("http_get(ioname,host,path,port,headers)") :(http_get_end)
http_get
port = ident(port) "80"
tcp.open(ioname, host, port, HTTP.RECL) :f(freturn)
$ioname = "GET " path " HTTP/1.0" HTTP.CRLF
+ "User-Agent: " HOST() HTTP.CRLF
+ "Host: " host HTTP.CRLF
+ headers
+ HTTP.CRLF :(return)
http_get_end
* move_tile
* curl 'http://slowass.net:11704/?x=1132&y=52&id=card018&action=move' -H 'Host: slowass.net:11704' -H 'User-Agent: Mozilla/5.0 (X11; OpenBSD amd64; rv:56.0) Gecko/20100101 Firefox/66.0' -H 'Accept: */*' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'Referer: http://slowass.net:11704/?action=board' -H 'Cookie: sid=42522100784659544515' -H 'DNT: 1' -H 'Connection: keep-alive'
define('move_tile(tile,x,y)') :(move_tile_end)
move_tile
output = 'move tile...'
;* getting scab-client.sno:127: Error 4 in statement 165 at level 2 Null string in illegal context on the http_get() line
output = ident(tile) 'tile is null'
http_get( .webfh, 'slowass.net', '/?action=move&id=' tile.id(tile) '&x=' x '&y=' y, 11704, cookies ) :f(http_error)
move_read
tmp = webfh :s(move_read)
http.close(.webfh)
:(return)
move_tile_end
* move_tile_to_board
define('move_tile_to_board(tile,x,y)') :(move_tile_to_board_end)
move_tile_to_board
move_tile( tile, 204 + ( ( x - 1 ) * 62), 268 + ( ( y - 1 ) * 61 ) ) ;* placing tiles on the board may need tweaking XX
:(return)
move_tile_to_board_end
* dump_rack
define('dump_rack()') :(dump_rack_done)
dump_rack
output = 'rack:'
i = 0
dump_rack_next
i = i + 1
tile = rack<i> :f(return)
letter = differ(tile) tile.letter( tile ) ;* if tile isn't null, get the letter off of it XXX put this in a subroutine maybe
letter = ident(tile) ' ' ;* if tile is null, use ' '
output = i ': ' letter :(dump_rack_next)
dump_rack_done
* dump_board
define('dump_board(board)') :(dump_board_done)
dump_board
y = 0
dump_board_y
y = lt(y, 15) y + 1 :f(return)
x = 0
out = ''
dump_board_x
tile = board<x = x + 1, y>
letter = differ(tile) tile.letter( tile ) ;* if tile isn't null, get the letter off of it
letter = ident(tile) ' ' ;* if tile is null, use ' '
out = out letter
output = eq(x, 15) out
lt(x, 15) :s(dump_board_x)f(dump_board_y)
dump_board_done
* find_tile_in_rack
define('find_tile_in_rack(letter),i,tile') :(find_tile_in_rack_end)
find_tile_in_rack
i = 0
find_tile_in_rack_next
tile = rack< i = i + 1 > :f(freturn) ;* out of positions in the rack to look at
differ(tile) :f(find_tile_in_rack_next) ;* skip to the next tile if this one is null
ident( tile.letter(tile), letter ) :f(find_tile_in_rack_next) ;* skip to the next tile if this isn't the one being searched for
find_tile_in_rack = tile
rack< i > = ''
:(return)
find_tile_in_rack_end
* draw_rack
* draw tiles as needed but return a string listing the letters we do have.
* caller needs to reload the board and call again to see what was drawn if we did draw things, so generally you'll do draw_rack(); read_board(); letters = draw_rack()
* takes from the tile pile (the sushi bag) unless we found tiles in our rack area that weren't in their exact spots, then we use those first
define('draw_rack()i,random_sushi,letters') :(draw_rack_done)
draw_rack
output = "drawing tiles..."
i = 0
random_sushi = -1
letters = ''
draw_rack_next
tile = rack< i = i + 1 > :f(draw_rack_last)
differ(tile) :f(draw_rack_1) ;* branch if tile is empty
letters = letters tile.letter(tile) :(draw_rack_next)
draw_rack_1
output = "drawing a tile for slot " i " last_sushi = " last_sushi
gt(last_rack_area) :f(draw_rack_1a)
output = "taking a tile from our rack area"
move_tile( rack_area< last_rack_area >, rack_x, rack_y + ( ( i - 1 ) * 60 ) )
last_rack_area = last_rack_area - 1 :(draw_rack_next) ;* in this case, the caller needs to reload the board and call us again
draw_rack_1a
gt(last_sushi) :s(draw_rack_2)
output = "no more sushi to draw!" :(draw_rack_next) ;* returning less than 7 letters is fine so this is just debug
draw_rack_2
random_sushi = 1 + remdr( random(), last_sushi )
output = ident( sushi< random_sushi > ) 'draw_rack: expected sushi in slot ' random_sushi ' but there is none'
move_tile( sushi< random_sushi >, 10, 10 ) ;* turn it over before moving it to our rack
move_tile( sushi< random_sushi >, rack_x, rack_y + ( ( i - 1 ) * 60 ) )
;* shuffle sushi array down to fill the gap
sushi< random_sushi > = sushi< last_sushi >
sushi< last_sushi > =
last_sushi = last_sushi - 1 ;* in this case, the caller needs to reload the board and call us again
:(draw_rack_next) ;* we don't get told what the letter is until we reload the board (else subscribe events)
draw_rack_last
draw_rack = letters :(return)
draw_rack_done
* read_board
* request the board from the server, and update rack, board, and sushi arrays, their sizes, and board_is_empty.
* we do this after moving tiles instead of trying to model our own concept of the game board across changes.
define('read_board()i,x,y') :(read_board_last)
read_board
output = "re-reading board..."
board = array('1:15,1:15')
sushi = array(100)
rack = array(7)
rack_area = array(7) ;*
last_board = 0
last_tiles = 0
last_sushi = 0
last_rack_area = 0
board_is_empty = 1
dog_placed = ''
http_get( .webfh, 'slowass.net', '/?action=board', 11704, cookies ) :f(http_error)
read_board_again
line = webfh :f(read_board_done)
* output = line
* <img id="card095" alt="card095" src="/jpg/_.png" style="position:absolute; left: 616px; top: -9px; z-index: 1396; width: 57px; height: 57px;" onload="Drag.init(this);" >
line '<img id="' arb . id '" alt="' arb '" src="/jpg/' arb . letter '.png" style="position:absolute; left: '
+ span(nums) . x 'px; top: ' span(nums) . y 'px; z-index'
+ :f(read_board_again)
* output = "found letter " letter " x " x " y " y " id " id
* the dog token
ident(id, 'dog') :f(board_sort_tiles_0)
dog_x = x; dog_y = y
output = "dog found"
dog = tile.new('#', 'dog')
lt(y, 85) gt(x, 1050) lt(x, 1211) :f(board_sort_tiles_0) ;* if the dog token is found and it is on its pad
output = "dog found and on the pad"
dog_placed = 1 :(read_board_again)
* undrawn tiles still in the pile
board_sort_tiles_0
lt( x, 750 ) lt( y, 125 ) gt( y, -30 ) :f(board_sort_tiles_1)
sushi< last_sushi = last_sushi + 1 > = tile.new(letter, id)
:(read_board_again)
board_sort_tiles_1
* tiles on the board
gt( x, 195 ) gt( y, 252 ) lt( x, 1104 ) lt( y, 1137 ) :f(board_sort_tiles_2)
* output = "board: prelim: tile " letter " is at " x " " y
x = 1 + ( ( x - 195 ) / 60 );
y = 1 + ( ( y - 252 ) / 59 );
* output = "board: tile " letter " is at " x " " y
board<x, y> = tile.new(letter, id)
board_is_empty = 0
:(read_board_again)
board_sort_tiles_2
* tiles in our rack
* eq( x, rack_x ) ge( y, rack_y ) le( y, rack_y + 60 * 7) :f(board_sort_tiles_3)
ge( x, rack_x - 30 ) le( x, rack_x + 30 ) ge( y, rack_y - 60 ) le( y, rack_y + 60 * 7 + 60 ) :f(board_sort_tiles_3)
output = "found a tile on our rack at y " y
eq( x, rack_x ) eq( remdr( y - rack_y, 60 ), 0 ) :f(board_sort_tiles_2a)
y = 1 + ( y - rack_y) / 60
output = " that's rack at position " y
rack<y> = tile.new(letter, id) :(read_board_again)
board_sort_tiles_2a
* okay, we have a tile in our rack area but it's misaligned
* make a note of it and then use it first when we next go to draw, which might be right away if there aren't 7 letters in the exact positions on the board to be in our rack
output = " in our rack area but not aligned"
rack_area< last_rack_area = last_rack_area + 1 > = tile.new(letter, id) :(read_board_again)
board_sort_tiles_3
output = 'XXX tile out of range: ' x ' ' y ' ' letter :(read_board_again) ;* unless bugs appear, this is fine... any out of range tile is just in someone's rack
read_board_done
http.close(.webfh) :(return)
read_board_last
* build_board_lines_arrays
define('build_board_lines_arrays(board)x,y,tile,pat,latter') :(build_board_lines_arrays_end)
build_board_lines_arrays
board_lines_pat = array(30, pos(0)) ;* 15 rows then 15 columns; play validation pattern
board_lines_text = array(30) ;* 15 rows then 15 columns; text of what's already on the board
y = 0
crawl_board_y
y = lt(y, 15) y + 1 :f(return)
x = 0
crawl_board_x
tile = board<x = x + 1, y>
pat = differ(tile) ( tile.letter(tile) | '*' ) ;* if tile isn't null, get the letter off of it and match that or '*'
pat = ident(tile) len(1) ;* if tile is null, use len(1)
board_lines_pat<y> = board_lines_pat<y> pat ;* rows
board_lines_pat<15 + x> = board_lines_pat<15 + x> pat ;* columns
letter = differ(tile) tile.letter( tile )
letter = ident(tile) ' '
board_lines_text<y> = board_lines_text<y> letter
board_lines_text<15 + x> = board_lines_text<15 + x> letter
* output = "debug: datatype board_lines y = " y " is " datatype(board_lines<y>)
lt(x, 15) :s(crawl_board_x)f(crawl_board_y)
build_board_lines_arrays_end
* count_invalid_words
* count the number of words arrays of strings that aren't in the dictionary.
* used so that we can make sure that something we're looking at isn't an illegal play.
* takes the output build_board_lines_arrays(), output the board_lines_text array.
define('count_invalid_words(arr),x,y,i,row,word') :(count_invalid_words_end)
count_invalid_words
num_invalid_words = 0 ;* we don't have to challenge the player but for now we don't want to add invalid plays
i = 0
verify_rows_next_row
row = arr<i = i + 1> :f(verify_rows_done)
verify_rows_next_word
row arb ( any(&lcase) span(&lcase) ) . word = '' :f(verify_rows_next_row)
output = "word to validate = " word
output = ident( dict<word> ) "invalid word >" word "<" ;* if dict<word> is null; debugging
num_invalid_words = ident( dict<word> ) num_invalid_words + 1 ;* if dict<word> is null
:(verify_rows_next_word)
verify_rows_done
output = "XXX number invalid words: " num_invalid_words
count_invalid_words = num_invalid_words :(return)
count_invalid_words_end
* longest_word
define('longest_word(arr)word,i') :(longest_word_end)
longest_word
i = 0
longest_word = ''
longest_word_next
word = arr< i = i + 1 > :f(return)
longest_word = gt( size(word), size(longest_word) ) word :(longest_word_next)
longest_word_end
* join_array
define('join_array(arr,sep)out,i') :(join_array_done)
join_array
sep = ident(sep) ' '
out = ''
i = 0
join_array_next
out = differ(out) out sep ;* if not empty, add a space
out = out arr< i = i + 1 > :s(join_array_next)
join_array = out :(return)
join_array_done
* play_on_board
* i 1-15 indexes the board as rows, and i 16-30 indexes the board was columns.
* that's squirrely, but it simplifies word fitting.
* coordinates and directions rotate 90 degrees for i > 15.
define('play_on_board(board,word,x,y,i,offset,go)next_letter,tile') :(play_on_board_done)
play_on_board
x = i - 15
x = le(i, 15) offset + 1
y = offset + 1
y = le(i, 15) i
x_delta = 0
x_delta = le(i, 15) 1 ;* i 0-15 indexes rows which correspond to moving left to right on the board
y_delta = 0
y_delta = gt(i, 15) 1 ;* i 15-30 indexes cols which correspond to moving from the top downwards on the board
play_on_board_1
word len(1) . next_letter = '' :f(return)
output = 'XXX putting letter ' next_letter ' at ' x ',' y
ident(go) :s(play_on_board_3) ;* if the go flag isn't set, skip actually moving the tile on the server
ident(board<x, y>) :f(play_on_board_4) ;* if the board is already occupied there, don't place a tile or change the board
move_tile_to_board( find_tile_in_rack(next_letter), x, y ) ;* if it is go time, update the actual server
play_on_board_3
board<x, y> = tile.new(next_letter, '')
play_on_board_4
x = x + x_delta
y = y + y_delta :(play_on_board_1)
play_on_board_done
*
* main
*
* figure out how many words are in the dictionary so we can allocate arrays for them
input('dictfh', 10, '', dictfile) :f(end)
read_dict0
dictsize = dictsize + 1
tmp = dictfh :s(read_dict0)
output = dictsize - 1 ' words in the dictionary'
* build dict, ana, ana_dict, dict_table from the dictionary
dict = table() ;* quickly lookup whether a word exists
ana = table() ;* given an anagram-normalized word (letters sorted), a space separated string of all possible anagrams
ana_dict = array(dictsize) ;* each word in anagram-normalized format with its letters sorted lexigraphically, used to find words for a set of tiles
dictsize = 1 ;* index into ana_dict while we're reading
rewind(10) ;* re-read the file. since arrays don't dynamically expand, this is done in two passes, and now we have arrays to put this in.
read_dict1
word = dictfh :f(read_dict2)
* dict<dictsize> = word ;* this used to be an array
sw = sortw(word)
dict<word> = sw
ana_dict<dictsize> = sw
ana<sw> = ana<sw> word ' ' ;* all possible words for a sorted set of letters
dictsize = dictsize + 1 :(read_dict1)
read_dict2
output = "dictsize: " dictsize
output = "host: " host()
http_get( .webfh, 'slowass.net', '/', 11704, 'Cookie: sid=' sid HTTP.CRLF ) :f(http_error)
login_page_read
tmp = webfh :f(login_page_read_done)
;* Set-Cookie: sid=83062304708249829056; path=/; expires=Sun, 13-Jan-2019 21:09:41 GMT
tmp "Set-Cookie: sid=" span(nums) . sid ';' :f(login_page_read)
output = "sid = " sid :(login_page_read)
login_page_read_done
http.close(.webfh)
cookies = 'Cookie: sid=' sid HTTP.CRLF
output = "login page..."
http_get( .webfh, 'slowass.net', '/?player_id=computer&password=fred&gamepass=wee', 11704, cookies ) :f(http_error)
login_read
tmp = webfh :s(login_read)
* output = webfh :s(login_read)
http.close(.webfh)
output = "welcome page..."
http_get( .webfh, 'slowass.net', '/', 11704, cookies ) :f(http_error)
welcome_page_read
tmp = webfh :s(welcome_page_read)
* output = webfh :s(welcome_page_read)
http.close(.webfh)
*
* start of main play loop
*
* we read the board, draw tiles as needed, make a play, then draw tiles again
board_read
output = "board page..."
read_board()
differ(dog_placed) :s(board_read_1)
output = "dog not in place, waiting" :(wait_for_the_human)
board_read_1
move_tile(dog, dog_x + 15, dog_y + 10 ) ;* move the little dog figurine just a little just so that the player knows we're here
output = "dog in place, playing"
* draw as needed
draw_rack()
read_board() ;* if draw_rack() did draw, then we need to update our rack, etc from the board
letters = draw_rack() ;* what we have in our hand after possibily drawing and reloading
output = "rack: " letters
gt(size(letters)) :f(end_of_game)
* dump the board
output = "board:"
dump_board(board)
* all possible plays with the letters in our rack
words = subpermutations(letters)
* smack something down onto the board if it's the first turn
eq(board_is_empty, 1) :f(board_is_not_empty)
output = "board is empty"
output = "all possible words: " join_array(words)
* word = subwords<1,1>
word = longest_word( words )
output = "playing " word
play_len = size(word)
y = 8
x = 8 - convert(play_len / 2, 'integer')
first_play
word pos(0) len(1) . letter = '' :f(first_play_last)
tile = find_tile_in_rack(letter) :f(tile_not_found_error)
output = "moving " letter " to " x " , " y
move_tile_to_board( tile, x, y )
x = x + 1 :(first_play)
first_play_last
draw_rack()
:(wait_for_the_human)
* board isn't empty so have to try to fit a play on somewhere
* start with a window size of 7 (or less, if our rack is smaller) -- we're looking for spans of blank tiles this big to play the same number of tiles in
* move left to right, top to bottom, looking for horizontal plays, then top to bottom, left to right, looking for vertical plays
* for any window we look at, if it borders letters on either side, include those letters but restrict results to things with letters in those positions
* if the word fits, make sure we haven't introduced non-words in the other play direction, then play it
* if we get to the end without fitting a word, try a smaller window size
board_is_not_empty
output = "board is not empty"
output = "all possible words with just what's in our rack: " join_array(words)
build_board_lines_arrays(board) ;* updates board_lines_pat, board_lines_text
* output = 'build_board_lines board_lines_text' join_array(board_lines_text, char(10) char(13)) ;* rows come first (moving left to right), then the columns (top downwards)
board_lines_text_cp = copy(board_lines_text) ;* we go back to these every time we mess things up
board_lines_pat_cp = copy(board_lines_pat)
num_invalid_words_pre = count_invalid_words(board_lines_text)
window_size = size(letters) ;* gets iteratively reduced
play_start
i = 0
play_next_line
offset = 0
i = i + 1
line_text = board_lines_text<i> :f(play_no_more_lines)
line_pat = board_lines_pat<i>
output = &line ' offset ' offset ' line ' i ' window_size ' window_size
play_next_offset
le( offset + window_size, 15 ) :f(play_next_line)
word_leading_window = ''
line_text pos(0) len( offset ) span( &lcase ) . word_leading_window
word_trailing_window = ''
line_text pos(0) len( offset ) len( size(word_leading_window) ) len( window_size ) span( &lcase ) . word_trailing_window
output = 'XXX window size: ' window_size ' offset: ' offset ' line ' i ' possible word leading our window: ' word_leading_window
+ ' possible word trailing: ' word_trailing_window
effective_window_size = window_size + size(word_leading_window) + size(word_trailing_window)
line_text pos(0) len( offset ) len( effective_window_size ) . subline_text :f(play_next_line) ;* window_size chars worth of spaces on the board in to subline_text
output = 'XXX play area >' subline_text '<'
play_2 subline_text span(' ') = '' :s(play_2) ;* for the sake of adding those letters to our effective rack, remove spaces
* XXX for now, require that we abut a word on the start or end or span a word or both, before trying to play a word in this location
* XXX ideally, letters on the previous or next line at least partially overlapping the play location would be adequate assuming the play doesn't add non-words to the board
output = 'XXX size of characters in window: ' size(subline_text) ' exact_size specified to subpermutations2: ' window_size + size(subline_text)
gt(size(subline_text)) :f(play_advance_offset)
;* also pass the pattern in and pre-filter by that
line_text len( offset ) . offset_text ;* offset_text is text of stuff already there before our offset
words = subpermutations2( letters subline_text, offset_text, line_pat, line_text, window_size + size(subline_text) )
output = "num words found: " words<0>
output = "words found that fit with those postfixes/prefixes: " join_array(words)
gt(words<0>) :f(play_advance_offset)
* XXX sort these descending by length? sort() will sort by a field in a multidim array.
* or else for now just iterate over them in their current arbitrary order trying to find one that fits at all.
word = longest_word( words )
* XXX yes, iterate on those
output = "okay, at least one word found... going to try to play " word
* this uses window_size instead of size(letters) because with shrunk window sizes, we won't use all of our letters
eq(size(word), window_size + size(subline_text)) :f(play_advance_offset) ;* XXX should maybe loop on other possible words instead
output = 'XXX trying to play word: ' word ' at offset ' offset ' line ' i
* can't do this unless unless we update both rows and cols at the same time
* so instead, we're putting the play on a copy of the board without updating the server, rebuilding board_lines_text from that,
* and then validating the play with count_invalid_words()
* board_lines_text_cp<i> pos(0) len(offset) len(word) = word
board_cp = copy(board)
play_on_board(board_cp, word, x, y, i, offset)
output = 'XXX board after experimental play:'
dump_board(board_cp)
build_board_lines_arrays(board_cp)
num_invalid_words_post = count_invalid_words(board_lines_text)
output = 'XXX post test-play num_invalid_words: ' num_invalid_words_post
eq(num_invalid_words_pre, num_invalid_words_post) :s(play_success)
* abort the play
board_lines_pat = board_lines_pat_cp
board_lines_text = board_lines_text_cp
:f(play_advance_offset) ;* XXXXX iterate over words instead, then play_advance_offset if exhausted
play_advance_offset
;* when moving the offset, move it past any letters that might appear on the board at that position
offset = offset + 1 + size(word_leading_window) :(play_next_offset)
play_no_more_lines
window_size = window_size - 1
ge(window_size, 1) :s(play_start)
output = "well, guess we didn't find anything." :(wait_for_the_human)
play_success
play_on_board(board, word, x, y, i, offset, 1)
draw_rack()
output = "success!?"
* wait for the human
wait_for_the_human
* output = "press enter..."
* tmp = input ;* wait for the human
ne(dog_x, 900) ne(dog_y, 10) move_tile(dog, 900, 10) ;* move the little dog figurine off the pad so that next loop, unless there is human intervention, we don't run... but if the dog is already there, don't create extra events
dog_placed = ''
output = "waiting..."
* host(1, 'sleep 10') ;* this returns instantly if we've gobbled up all available memory. yup, can't get this to work worth a damn.
generation = ident(generation) 0 ;* default to 0. server tells us the current value, which is the number of tile moves to date.
;* /updator?action=updator returns immediately if our generation is less than current, waits until something moves
http_get( .webfh, 'slowass.net', '/updator?action=updator&player_id=computer&generation=' generation, 11704, cookies ) :f(http_error)
wait_read
tmp = webfh :f(wait_read_done)
* output = tmp
tmp 'sync: ' span(nums) . generation ;* matches: Alpha/testing Illuminati server / sync: 100
:(wait_read)
wait_read_done
http.close(.webfh)
output = 'generation ' generation
:(board_read) ;* loop
*
* error messages
*
end_of_game
output = "no more tiles! good game!" :(wait_for_the_human) ;* soft-error as the player can always reset the board and ask us to go again
http_error
output = "HTTP something failed" :(end) ;* would be nice to deal with this more gracefully... at least we could re-try the play
tile_not_found_error
output = "find_tile_in_rack couldn't locate the tile we wanted" :(end) ;* shouldn't happen
end
* snobol4 -d 100M scab.sno
-include 'random.sno'
-include 'host.sno'
srandomdev()
* dictfile = 'twl06.txt'
dictfile = 'twl06.txt.test'
* sort letters of word
define('sortw(str)a,i,j') :(sortw_end)
sortw a = array(size(str))
sw1 i = i + 1; str len(1) . a<i> = :s(sw1)
a = sort(a)
sw2 j = j + 1; sortw = sortw a<j> :s(sw2)f(return)
sortw_end
* subpermutations
define('subpermutations(str,res,stop)i,s1,s2') :(subpermutations_end)
subpermutations
i = size(str)
gt(i, 1) :f(subpermutations_last)
subpermutations_again
str len(1) . s1 rem . s2
* output = "XXX s1 = " s1 " s2 = " s2 ;* XXX still need to make sure we're doing all permutations
sorted_s2 = sortw(s2)
eq( res< sorted_s1 >, 1 ) :s(subpermutations_next) ;* don't recurse in to here again if we've already done so once
res< sorted_s2 > = 1
str = s2 s1 ;* stuff the character we picked off of the front back on to the end
subpermutations(s2, res, stop) ;* recurse
subpermutations_next
i = i - 1
gt(i, 0) :s(subpermutations_again)
subpermutations_last
subpermutations = res :(return) ;* return the table of all anagram stems
subpermutations_end
* subpermutations2
define('subpermutations2(str)strlen,sw,i,s1,s2,pat,res') :(subpermutations2_end)
subpermutations2
collect()
res = table()
i = 0
pat = pos(0) ;* pattern gets built as a function of letters in the sorted word we're looking searching for matches for
subpermutations2_1
str len(1) . s1 rem . str :f(subpermutations2_2)
* output = "subpermutations2 s1 = " s1
* pat = pat arbno(s1) :(subpermutations2_1) ;* really want 0-1 of these instead of 0+
pat = pat ( s1 | '' ) :(subpermutations2_1)
subpermutations2_2
pat = pat pos( *strlen ) ;* force it to match through to the end. the * defers evaluation of strlen and we update this value each loop iteration.
subpermutations2_3
i = i + 1
sw = ana_dict<i> :f(subpermutations2_last)
strlen = size(sw)
sw pat :f(subpermutations2_3)
res< sw > = 1 :(subpermutations2_2)
subpermutations2_last
subpermutations2 = res :(return)
subpermutations2_end
* main
input('dictfh', 10, '', dictfile)
read_dict0
dictsize = dictsize + 1
tmp = dictfh :s(read_dict0)
output = dictsize - 1 ' words in the dictionary'
dict = array(dictsize)
ana_dict = array(dictsize) ;* the dictionary but each word in anagram-normalized format with its letters sorted lexigraphically
dictsize = 1
ana = table() ;*
* input('dictfh', 10, '', dictfile) ;* set doesn't seem to work so just re-open it
rewind(10) ;* XXXX trying this instead
read_dict1
word = dictfh :f(read_dict2)
dict<dictsize> = word
sw = sortw(word)
ana_dict<dictsize> = sw
ana<sw> = ana<sw> word ' ' ;* all possible words for a sorted set of letters
dictsize = dictsize + 1 :(read_dict1)
read_dict2
output = "dictsize: " dictsize
* pick a random word
pick_random_word
rand = random()
rand = rand - dictsize * convert( rand / dictsize, 'integer' ) ;* modulo XXXX use remdr() function
word = dict< rand >
* output = "word: " word
output = ''
output = "tiles: " sortw(word)
tmp = input
output = ana< sortw(word) >
host(1, "dict " word)
* list all of the different words that could be made from those letters
* subwords = subpermutations(word, table())
subwords = subpermutations2(sortw(word))
subwords = convert( subwords, 'ARRAY' )
* output = "subwords datatype2: " datatype(subwords)
i = 0
outstr = ''
next_subword
i = i + 1
subwords<i,1> :f(subwords_last)
* output = "looking up anagrams for " subwords<i,1> ;* should already be in sorted order...
* lt(i, size(subwords)) :s(next_subword) ;* no function to easily find array dimension other than prototype? guess not!
outstr = outstr ana< subwords<i,1> > ' ' :s(next_subword)
subwords_last
outstr ' ' = ' ' :s(subwords_last) ;* we accumulated a lot of extra spaces so fold those down to single spaces between words
output = "all possible words: " outstr
:(pick_random_word)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment