Last active
January 16, 2019 19:52
-
-
Save scottwalters/56d270bbdf0d7560644a6e0ff209810a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
* 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 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
* 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