{- Copyright 2008 by Ken Takusagawa This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. -} module Main (main) where{ import IO; import System.IO.Unsafe; import Control.Monad.ST; import Control.Monad.Error; import Control.Monad.State; import qualified Data.Set as Set; import Data.Array.ST; import Maybe; import Random; import Char; import System; import Data.STRef; import Data.Array.IArray; import Data.Ord; import Control.Parallel.Strategies; import Data.List; main :: IO(()); main = (do{ (getArgs >>= (\lambda_case_var ->case lambda_case_var of { ["nothing"]-> (return ()); ["template"]-> template; ["do-move"]-> do_move; ["go", (x)]-> (go (read x)); ["gob", (x)]-> (no_blank (read x)); ["show-test"]-> (putStr (show_board test_board show_mchar)) })); }); show_list :: (Show (a)) => [](a) -> String; show_list l = (unlines (map show l)); zip_map :: (a -> b) -> [](a) -> []((a, b)); zip_map f l = (zip l (map f l)); zip_map_parallel :: Strategy(b) -> (a -> b) -> [](a) -> []((a, b)); zip_map_parallel strat f l = (zip l (using (map f l) (parList strat))); map_tuple :: (a -> b) -> (a, a) -> (b, b); map_tuple fn x = ((fn (fst x)), (fn (snd x))); reverse_comparison :: Ordering -> Ordering; reverse_comparison o = (case o of { (LT)-> GT; (EQ)-> EQ; (GT)-> LT }); reverse_comparison_function :: (a -> a -> Ordering) -> (a -> a -> Ordering); reverse_comparison_function f = (curry((reverse_comparison . (uncurry f)))); zip_check_same_length :: [](a) -> [](b) -> []((a, b)); zip_check_same_length x1 x2 = (case (x1, x2) of { (([]), ([]))-> []; (((:)(a) (arest)), ((:)(b) (brest)))-> ((:) (a, b) (zip_check_same_length arest brest)) }); zipWith_check_same_length :: (a -> b -> c) -> [](a) -> [](b) -> [](c); zipWith_check_same_length f x1 x2 = (case (x1, x2) of { (([]), ([]))-> []; (((:)(a) (arest)), ((:)(b) (brest)))-> ((:) (f a b) (zipWith_check_same_length f arest brest)) }); apply_first :: (a -> b) -> (a, c) -> (b, c); apply_first fn x = ((fn (fst x)), (snd x)); apply_second :: (a -> b) -> (c, a) -> (c, b); apply_second fn x = ((fst x), (fn (snd x))); uniform_random_IO :: IO(Float); uniform_random_IO = (randomRIO (0, 1)); random_permutation_IO :: [](a) -> IO([](a)); random_permutation_IO l = ((sequence (replicate (length l) uniform_random_IO)) >>= (return . (map fst) . (sortBy (comparing snd)) . (zip_check_same_length l))); filter_justs :: ([](Maybe(a)) -> [](a)); filter_justs = ((map fromJust) . (filter isJust)); rcs_code :: String; rcs_code = "$Id$"; newtype RRow = RRow(Int) deriving (Ix, Ord, Eq, Enum, Show); newtype CCol = CCol(Int) deriving (Num, Show, Eq, Ix, Ord, Enum, NFData); type Coord = (RRow, CCol); type RC_board a = Array(RRow)(Array(CCol)(a)); type Board = VV_board(Char); type VV_board a = RC_board(Maybe(a)); type Before_and_after = (String, String); type MString = [](Mchar); mmatch :: (Eq (char)) => char -> Maybe(char) -> Maybe(Maybe(char)); mmatch c r = (case r of { (Just(rx))-> (case ((==) c rx) of { (True)-> (Just (Nothing )); (_)-> Nothing }); (Nothing)-> (Just (Just (c ))) }); mfits_half_row :: String -> MString -> Maybe([](Maybe(Char))); mfits_half_row word row = (mapM (uncurry mmatch) (zip_check_same_length word row)); type Row = Array(CCol)(Mchar); type Mchar = Maybe(Char); mmatch_row :: Before_and_after -> Row -> CCol -> Maybe(Put_letters); mmatch_row word row position = (let { p_left = ((-) position (CCol (length (fst word)))); p_right = ((+) position (CCol (length (snd word)))) } in (do{ (guard ((<=) (fst (bounds row)) p_left)); (guard ((||) ((==) p_left (fst (bounds row))) (isNothing ((!) row (pred p_left))))); left :: [](Mchar) <- (mfits_half_row (fst word) (array_subrange row p_left (pred position))); (guard ((<=) p_right (snd (bounds row)))); (guard ((||) ((==) p_right (snd (bounds row))) (isNothing ((!) row (succ p_right))))); right :: [](Mchar) <- (mfits_half_row (snd word) (array_subrange row (succ position) p_right)); (guard ((||) (not (null (filter isJust left))) (not (null (filter isJust right))))); (return ((p_left, p_right), (get_justs((zip_check_same_length (enumFromTo p_left p_right))((left ++ [Nothing] ++ right)))))); })); array_subrange :: (Enum (i), Ix (i)) => Array(i)(a) -> i -> i -> [](a); array_subrange v start end = ((map ((!) v))((enumFromTo start end))); read_mchar :: Char -> Mchar; read_mchar c = (case c of { ('.')-> Nothing; (x)-> (Just x) }); mkrow :: (a -> b) -> [](a) -> Array(CCol)(b); mkrow f s = (listArray ((CCol 1), (CCol (length s))) (map f s)); mkboard :: (a -> b) -> []([](a)) -> RC_board(b); mkboard f ss = (listArray ((RRow 1), (RRow (length ss))) (map (mkrow f) ss)); test_board :: Board; test_board = (fst (make_test_board "....drip")); make_test_board :: String -> Board_state; make_test_board first_move = (let { test_board :: Board; test_board = (transpose_board((mkboard read_mchar)(((replicate 7 "...............") ++ [(take 15 ((++) first_move (repeat '.')))] ++ (replicate 7 "..............."))))) } in (make_board_state test_board (blankless_board_points test_board))); check_a_orthogonal :: (Enum (ix), Ord (ix)) => Is_word -> (ix -> Mchar) -> (ix, ix) -> ix -> Char -> Maybe(Maybe((ix, ix))); check_a_orthogonal is_word column bounds_column i c = (let { up = (follow_pred column (fst bounds_column) i); down = (follow_succ column (snd bounds_column) i) } in (case ((==) up down) of { (True)-> (Just Nothing); (_)-> (do{ (guard(is_word((((map (fromJust . column))((enumFromTo up (pred i)))) ++ [c] ++ ((map (fromJust . column))((enumFromTo (succ i) down))))))); (return (Just (up, down))); }) })); follow_pred :: (Enum (ix), Ord (ix)) => (ix -> Maybe(a)) -> ix -> ix -> ix; follow_pred row min p = (case (compare min p) of { (EQ)-> p; (LT)-> (case (row (pred p)) of { (Nothing)-> p; (_)-> (follow_pred row min (pred p)) }) }); follow_succ :: (Enum (ix), Ord (ix)) => (ix -> Maybe(a)) -> ix -> ix -> ix; follow_succ row max p = (case (compare p max) of { (EQ)-> p; (LT)-> (case (row (succ p)) of { (Nothing)-> p; (_)-> (follow_succ row max (succ p)) }) }); type Put_letters = ((CCol, CCol), Put_letters_list); type Put_letters_list = []((CCol, Char)); type Horizontal_result = (RRow, Put_letters); type Resulting_words = (Horizontal_result, [](Vertical)); type Vertical = (CCol, (RRow, RRow)); type Play = (Resulting_words, Tiles_you_have); board_check_word :: Is_word -> Board -> RRow -> CCol -> Before_and_after -> Maybe(Resulting_words); board_check_word is_word horizontals row col word = (do{ put_letters :: Put_letters <- (mmatch_row word ((!) horizontals row) col); created_vertical_words :: []((CCol, Maybe((RRow, RRow)))) <- (mapM (uncurry (check_vertical is_word horizontals row)) (snd put_letters)); (return ((row, put_letters), (get_justs created_vertical_words))); }); get_justs :: []((a, Maybe(b))) -> []((a, b)); get_justs created_vertical_words = ((map (apply_second fromJust))((filter (isJust . snd))(created_vertical_words))); check_vertical :: Is_word -> Board -> RRow -> CCol -> Char -> Maybe((CCol, Maybe((RRow, RRow)))); check_vertical is_word horizontals row column c = (do{ updown :: Maybe((RRow, RRow)) <- (check_a_orthogonal is_word (transpose_vv horizontals column) (bounds horizontals) row c); (return (column, updown)); }); transpose_vv :: (Ix (r), Ix (c)) => Array(r)(Array(c)(a)) -> c -> r -> a; transpose_vv vv y x = ((!) ((!) vv x) y); transpose_board :: forall a . RC_board(a) -> RC_board(a); transpose_board b = (let { left_right :: (CCol, CCol); left_right = (bounds(((!) b)(fst(bounds(b))))); to_rrow :: CCol -> RRow; to_rrow c = (case c of { (CCol(x))-> (RRow x) }); to_ccol :: RRow -> CCol; to_ccol r = (case r of { (RRow(x))-> (CCol x) }); create_column :: CCol -> Array(CCol)(a); create_column col = (let { get :: Array(CCol)(a) -> a; get r = ((!) r col) } in ((listArray((map_tuple to_ccol)(bounds(b))))((map get)(elems(b))))) } in ((listArray (map_tuple to_rrow left_right))((map create_column)(range(left_right))))); go_test_board :: Before_and_after -> Int -> Int -> Maybe(Resulting_words); go_test_board word row col = (board_check_word (const True) test_board (RRow row) (CCol col) word); type Underflow = (); instance Error (Underflow) where { noMsg = () } ; process_letter :: STRef(s)(Int) -> STArray(s)(Char)(Int) -> Char -> ErrorT(Underflow)(ST(s))(()); process_letter pnum_blanks letters c = ((lift(runErrorT((countdown_array letters)(c)))) >>= (\lambda_case_var ->case lambda_case_var of { (Right(_))-> (return ()); (Left(_))-> ((lift (readSTRef pnum_blanks)) >>= (\lambda_case_var ->case lambda_case_var of { (0)-> (throwError ()); (num_blanks)-> (lift((writeSTRef pnum_blanks)(pred(num_blanks)))) })) })); type Tiles_you_have = (Int, Array(Char)(Int)); process_word :: Tiles_you_have -> String -> Maybe(Tiles_you_have); process_word current_tiles w = (case (runST(runErrorT((do{ pnum_blanks :: STRef(s)(Int) <- (lift (newSTRef (fst current_tiles))); pletters :: STArray(s)(Char)(Int) <- (lift (thaw (snd current_tiles))); (mapM_ (process_letter pnum_blanks pletters) w); blanks_left :: Int <- (lift (readSTRef pnum_blanks)); letters_left :: Array(Char)(Int) <- (lift (freeze pletters)); (return (blanks_left, letters_left)); })))) of { (Left(_))-> Nothing; (Right(x))-> (Just x) }); mkarray :: String -> Array(Char)(Int); mkarray s = (let { accum_func :: Int -> Int -> Int; accum_func old _ = (succ old) } in (accumArray accum_func 0 alphabet (zip s (repeat undefined)))); type Is_word = (String -> Bool); parallel_word_check_vertical :: Is_word -> Board -> RRow -> Put_letters_list -> Maybe([](Vertical)); parallel_word_check_vertical is_word horizontals row put_letters = ((mapM (uncurry (check_vertical is_word horizontals row)) put_letters) >>= (return . get_justs)); is_open_area :: CCol -> VV_board(a) -> RRow -> CCol -> Bool; is_open_area width b i j = (isJust (do{ (guard ((<=) (pred ((+) j width)) (snd(bounds(((!) b)(i)))))); (mapM_ (guard . (empty_at b i)) (enumFromTo (pred j) ((+) j width))); (let { word_range :: [](CCol); word_range = (enumFromTo j (pred ((+) j width))) } in (guard (not ((&&) (and (map (empty_at b (pred i)) word_range)) (and (map (empty_at b (succ i)) word_range)))))); (return ()); })); empty_at :: VV_board(a) -> RRow -> CCol -> Bool; empty_at b i j = (not ((&&) (inRange (bounds b) i) ((&&) (inRange (bounds ((!) b i)) j) (isJust ((!) ((!) b i) j))))); all_board_coordinates :: VV_board(a) -> [](Coord); all_board_coordinates b = (do{ r :: RRow <- (range (bounds b)); c :: CCol <- (range(bounds(((!) b)(fst(bounds(b)))))); (return (r, c)); }); find_open_areas :: VV_board(a) -> CCol -> [](Coord); find_open_areas b width = (filter (uncurry (is_open_area width b)) (all_board_coordinates b)); type Open_areas = Array(CCol)([](Coord)); max_tiles :: Int; max_tiles = 7; find_all_open_areas :: VV_board(a) -> Open_areas; find_all_open_areas b = (listArray ((CCol 1), (CCol max_tiles)) (map ((find_open_areas b) . CCol) (enumFromTo 1 max_tiles))); spellable_t :: Is_word -> Board -> RRow -> CCol -> Tiles_you_have -> Before_and_after -> Maybe(Play); spellable_t is_word b row col current_tiles word = (do{ resulting :: Resulting_words <- (board_check_word is_word b row col word); next_tiles :: Tiles_you_have <- (process_word current_tiles ((map snd)(snd(snd(fst(resulting)))))); (return (resulting, next_tiles)); }); do_t :: [](Before_and_after) -> Is_word -> Board -> Tiles_you_have -> RRow -> CCol -> [](Play); do_t words is_word b current_tiles row col = (sort(filter_justs((map (spellable_t is_word b row col current_tiles))(words)))); left_is_clear_point :: VV_board(a) -> RRow -> CCol -> Bool; left_is_clear_point b row col = ((&&) (not (empty_at b row col)) (empty_at b row (pred col))); show_board :: RC_board(a) -> (a -> String) -> String; show_board b f = (unlines((map (show_board_row f))(elems(b)))); show_board_row :: (a -> String) -> Array(CCol)(a) -> String; show_board_row f row = ((concatMap f)(elems(row))); show_mchar :: Mchar -> String; show_mchar m = (case m of { (Nothing)-> "."; (Just(c))-> [c] }); map_ix_array :: (Ix (ix)) => (ix -> a -> b) -> Array(ix)(a) -> Array(ix)(b); map_ix_array f v = (listArray (bounds v) (map (uncurry f) (assocs v))); map_rc :: (RRow -> CCol -> a -> b) -> RC_board(a) -> RC_board(b); map_rc f vv = (map_ix_array (map_rc_row f) vv); map_rc_row :: (RRow -> CCol -> a -> b) -> RRow -> Array(CCol)(a) -> Array(CCol)(b); map_rc_row f r v = (map_ix_array (f r) v); go_do_t :: B_a_list -> Is_word -> Board -> Tiles_you_have -> RRow -> CCol -> [](Play); go_do_t b_a_list is_word b current_tiles row col = (do_t (build_before_and_after ((!) b_a_list (fromJust ((!) ((!) b row) col)))) is_word b current_tiles row col); type B_a_list = Array(Char)(B_a_internal); type B_a_internal = [](Before_and_after); build_before_and_after :: B_a_internal -> [](Before_and_after); build_before_and_after int = int; words_io :: IO([](String)); words_io = ((>>=) (readFile "dictionary") (return . lines)); all_words :: [](String); all_words = (unsafePerformIO words_io); all_words_set :: Set.Set(String); all_words_set = (Set.fromList all_words); all_is_word :: String -> Bool; all_is_word w = (Set.member w all_words_set); split_at_char :: Char -> String -> String -> [](Before_and_after); split_at_char c pre l = (case l of { ([])-> []; ((:)(h) (t))-> (let { do_rest :: [](Before_and_after); do_rest = (split_at_char c ((:) h pre) t) } in (case ((==) h c) of { (True)-> ((:) ((reverse pre), t) do_rest); (_)-> do_rest })) }); alphabet :: (Char, Char); alphabet = ('a', 'z'); get_all_splits :: [](String) -> Char -> [](Before_and_after); get_all_splits words c = (words >>= (split_at_char c "") >>= return); get_ba_list :: [](String) -> B_a_list; get_ba_list words = (listArray alphabet (map (get_all_splits words) (range alphabet))); all_ba_list :: B_a_list; all_ba_list = (get_ba_list all_words); place_parallel_word :: Tiles_you_have -> Open_areas -> Is_word -> Board -> [](String) -> [](Play); place_parallel_word current_tiles open_spaces is_word b all_words = (do{ word :: String <- all_words; tiles_left :: Tiles_you_have <- (maybeToList (process_word current_tiles word)); where_to_put :: Coord <- ((!) open_spaces (CCol (length word))); (let { put_letters :: Put_letters; put_letters = (((snd where_to_put), (pred ((+) (snd where_to_put) (CCol (length word))))), (zip (enumFrom (snd where_to_put)) word)) } in (do{ verticals :: [](Vertical) <- (maybeToList (parallel_word_check_vertical is_word b (fst where_to_put) (snd put_letters))); (return ((((fst where_to_put), put_letters), verticals), tiles_left)); })); }); scan_orthogonal_plays :: Board -> Tiles_you_have -> [](Play); scan_orthogonal_plays b current_tiles = (do{ rc :: Coord <- (all_board_coordinates b); (guard (uncurry (left_is_clear_point b) rc)); ((uncurry (go_do_t all_ba_list all_is_word b current_tiles) rc) >>= return); }); scan_parallel_plays :: Board -> Tiles_you_have -> [](Play); scan_parallel_plays b current_tiles = (place_parallel_word current_tiles (find_all_open_areas b) all_is_word b all_words); scan_plays_one_board :: Board -> Tiles_you_have -> [](Play); scan_plays_one_board b current_tiles = ((++) (scan_orthogonal_plays b current_tiles) (scan_parallel_plays b current_tiles)); newtype Points = Points(Int) deriving (Num, Show, Eq, NFData); instance Ord (Points) where { compare x y = (case (x, y) of { ((Points(x1)), (Points(y1)))-> (case (compare x1 y1) of { (LT)-> GT; (EQ)-> EQ; (GT)-> LT }) }) } ; score_letter :: STArray(s)(Char)(Int) -> Char -> ST(s)(Points); score_letter letters c = ((runErrorT (do{ (countdown_array letters c); (return (get_points c)); })) >>= (\lambda_case_var ->case lambda_case_var of { (Left(_))-> (return points_blank); (Right(s))-> (return s) })); countdown_array :: (Ix (char), Enum (int), Num (int)) => STArray(s)(char)(int) -> char -> ErrorT(Underflow)(ST(s))(()); countdown_array letters c = ((lift (readArray letters c)) >>= (\lambda_case_var ->case lambda_case_var of { (0)-> (throwError ()); (num_that_letter)-> (lift((writeArray letters c)(pred(num_that_letter)))) })); points_blank :: Points; points_blank = (Points 0); get_points :: Char -> Points; get_points c = ((!) point_values_array c); point_values_array :: Array(Char)(Points); point_values_array = (let { simple_point_values :: []((Int, String)); simple_point_values = [(2, "dg"), (3, "bcmp"), (4, "fhvwy"), (5, "k"), (8, "jx"), (10, "qz")]; expand_simple_point_values :: (Int, String) -> []((Char, Points)); expand_simple_point_values f = (do{ c :: Char <- (snd f); (return (c, (Points (fst f)))); }) } in (accumArray (flip const) (Points 1) alphabet (concatMap expand_simple_point_values simple_point_values))); newtype Letter_multiplier = Letter_multiplier(Int) deriving (Ord, Eq); board_letter_multiplier :: RC_board(Letter_multiplier); board_letter_multiplier = (let { char_mult :: Char -> Letter_multiplier; char_mult c = (Letter_multiplier (case c of { ('c')-> 2; ('b')-> 3; (_)-> 1 })) } in (mkboard char_mult master_board)); board_word_multiplier :: RC_board(Word_multiplier); board_word_multiplier = (let { word_mult :: Char -> Word_multiplier; word_mult c = (Word_multiplier (case c of { ('r')-> 3; ('p')-> 2; (_)-> 1 })) } in (mkboard word_mult master_board)); board_width :: (CCol, CCol); board_width = ((CCol 1), (CCol(length(head(master_board))))); master_board :: [](String); master_board = (flip_double((map flip_double)(["r..c...r", ".p...b..", "..p...c.", "c..p...c", "....p...", ".b...b..", "..c...c.", "r..c...p"]))); flip_double :: [](a) -> [](a); flip_double l = ((++) l (reverse(init(l)))); expand_row_cols :: RRow -> [](CCol) -> [](Coord); expand_row_cols r cs = (let { make :: CCol -> Coord; make c = (r, c) } in (map make cs)); expand_col_rows :: CCol -> [](RRow) -> [](Coord); expand_col_rows c cs = (let { make :: RRow -> Coord; make r = (r, c) } in (map make cs)); type Coord_char = (Coord, Char); lookup_rc :: RC_board(a) -> Coord -> a; lookup_rc b xy = ((!) ((!) b (fst xy)) (snd xy)); score_word :: Tiles_you_have -> String -> [](Points); score_word current_tiles w = (let { in_st :: forall s . ST(s)([](Points)); in_st = (do{ pletters :: STArray(s)(Char)(Int) <- (thaw (snd current_tiles)); ret :: [](Points) <- (mapM (score_letter pletters) w); (return ret); }) } in (runST in_st)); reformat_horizontal_result :: Horizontal_result -> [](Coord_char); reformat_horizontal_result r = (zip_check_same_length ((expand_row_cols (fst r))((map fst)(snd(snd(r))))) ((map snd)(snd(snd(r))))); type Point_col = Array(CCol)(Points); point_array_row :: Tiles_you_have -> Horizontal_result -> Point_col; point_array_row current_tiles r = (let { sorted :: [](Coord_char); sorted = ((map fst)((sortBy (reverse_comparison_function (comparing snd)))((zip_map ((lookup_rc board_letter_multiplier) . fst))(reformat_horizontal_result(r))))) } in ((accumArray (flip const) bad_points board_width)((map (apply_first snd))((zip_check_same_length ((map fst)(sorted)) ((score_word current_tiles)((map snd)(sorted)))))))); bad_points :: Points; bad_points = (head [(error "bad-points: should never look at this"), (Points (negate 999))]); newtype Word_multiplier = Word_multiplier(Int); type Board_points = VV_board(Points); get_board_points :: Board_points -> [](Coord) -> Points; get_board_points board_points l = (sum(filter_justs((map (lookup_rc board_points))(l)))); score_play_horiz :: Board_points -> Point_col -> Horizontal_result -> Points; score_play_horiz board_points point_col h = (let { reformatted :: [](Coord); reformatted = ((map fst)(reformat_horizontal_result(h))); already_board_points :: Points; already_board_points = ((get_board_points board_points)((expand_row_cols (fst(h)) (range (fst(snd(h))))))); word_mult :: [](Word_multiplier); word_mult = ((map (lookup_rc board_word_multiplier))(reformatted)); letter_mult :: [](Letter_multiplier); letter_mult = ((map (lookup_rc board_letter_multiplier))(reformatted)); cols :: [](Points); cols = ((map ((!) point_col))((map snd)(reformatted))) } in (foldl mult_word (((+) already_board_points)(sum((zipWith_check_same_length mult_letter cols letter_mult)))) word_mult)); mult_letter :: Points -> Letter_multiplier -> Points; mult_letter p m = (case m of { (Letter_multiplier(l))-> ((*) p (Points l)) }); mult_word :: Points -> Word_multiplier -> Points; mult_word p m = (case m of { (Word_multiplier(l))-> ((*) p (Points l)) }); score_play_vertical :: Board_points -> (RRow, RRow) -> RRow -> CCol -> Points -> Points; score_play_vertical board_points rows row col point_at = (let { already_board_points :: Points; already_board_points = ((get_board_points board_points)((expand_col_rows col (range rows)))); coord :: Coord; coord = (row, col) } in (mult_word ((+) already_board_points (mult_letter point_at (lookup_rc board_letter_multiplier coord))) (lookup_rc board_word_multiplier coord))); go_score_play_vertical :: Board_points -> Point_col -> RRow -> Vertical -> Points; go_score_play_vertical board_points point_col row v = (score_play_vertical board_points (snd v) row (fst v) ((!) point_col (fst v))); score_all_verticals :: Board_points -> Point_col -> Resulting_words -> Points; score_all_verticals board_points point_col result = (sum((map (go_score_play_vertical board_points point_col (fst(fst(result)))))(snd(result)))); score_result :: Tiles_you_have -> Board_points -> Resulting_words -> (Points, Point_col); score_result current_tiles board_points result = (let { point_col :: Point_col; point_col = (point_array_row current_tiles (fst result)); bingo_bonus_points :: Points; bingo_bonus_points = (Points (case ((==) max_tiles (length(snd(snd(fst(result)))))) of { (True)-> 50; (_)-> 0 })) } in (((+) bingo_bonus_points ((+) ((score_play_horiz board_points point_col)(fst(result))) ((score_all_verticals board_points point_col)(result)))), point_col)); blankless_board_points :: Board -> Board_points; blankless_board_points b = (map_rc get_blankless_board_points b); get_blankless_board_points :: RRow -> CCol -> Mchar -> Maybe(Points); get_blankless_board_points _ _ x = (x >>= (return . get_points)); update_board_2 :: RRow -> Put_letters_list -> Board -> Board; update_board_2 row pl b = ((//) b [(row, (update_board_row ((!) b row) pl))]); double_transposition :: (RC_board(a) -> RC_board(a)) -> RC_board(a) -> RC_board(a); double_transposition f b = (transpose_board(f(transpose_board(b)))); update_board :: Orientation -> Board -> RRow -> Put_letters_list -> Board; update_board ot b row pl = (case ot of { (Left_right)-> (update_board_2 row pl b); (Up_down)-> (double_transposition (update_board_2 row pl) b) }); update_board_row :: Row -> Put_letters_list -> Row; update_board_row row pl = ((//) row ((map (apply_second Just))(pl))); update_board_points_3 :: RRow -> []((CCol, Points)) -> Board_points -> Board_points; update_board_points_3 row newcol b = ((//) b [(row, ((//) ((!) b row) (map (apply_second Just) newcol)))]); update_board_points :: Orientation -> Board_points -> RRow -> []((CCol, Points)) -> Board_points; update_board_points ot b row newcol = (case ot of { (Left_right)-> (update_board_points_3 row newcol b); (Up_down)-> (double_transposition (update_board_points_3 row newcol) b) }); type Board_state = (Board, (Board_points, ())); make_board_state :: Board -> Board_points -> Board_state; make_board_state b bp = (b, (bp, ())); unsorted_plays :: Tiles_you_have -> Board_state -> [](Essence_of_play); unsorted_plays current_tiles bs = (let { b :: Board; b = (fst(bs)); board_points :: Board_points; board_points = (fst(snd(bs))); plays_and_scores :: [](Essence_of_play); plays_and_scores = ((map (extract_essense Left_right))((zip_map_parallel rnf ((score_result current_tiles board_points) . fst))((scan_plays_one_board b current_tiles)))); plays_and_scores_2 :: [](Essence_of_play); plays_and_scores_2 = ((map (extract_essense Up_down))((zip_map_parallel rnf ((score_result current_tiles (transpose_board board_points)) . fst))((scan_plays_one_board (transpose_board b) current_tiles)))) } in ((++) plays_and_scores plays_and_scores_2)); update_board_state :: Board_state -> Essence_of_play -> Board_state; update_board_state bs best_play = (make_board_state (update_board (fst(fst(snd(best_play)))) (fst(bs)) (fst(snd(fst(snd(best_play))))) ((map (apply_second fst))(snd(snd(fst(snd(best_play))))))) (update_board_points (fst(fst(snd(best_play)))) (fst(snd(bs))) (fst(snd(fst(snd(best_play))))) ((map (apply_second snd))(snd(snd(fst(snd(best_play)))))))); best_single_play :: Tiles_you_have -> Board_state -> (Essence_of_play, Board_state); best_single_play current_tiles bs = (let { best_play :: Essence_of_play; best_play = (head((sortBy (comparing fst))((unsorted_plays current_tiles bs)))) } in (best_play, (update_board_state bs best_play))); data Orientation = Left_right | Up_down deriving (Show); type Ot a = (Orientation, a); type Essence_of_play_1 = Ot((RRow, []((CCol, (Char, Points))))); type Essence_of_play = (Points, (Essence_of_play_1, Tiles_you_have)); extract_essense :: Orientation -> (Play, (Points, Point_col)) -> Essence_of_play; extract_essense ot x = (let { mmm :: CCol -> Char -> (CCol, (Char, Points)); mmm pos c = (pos, (c, ((!) (snd(snd(x))) pos))) } in ((fst(snd(x))), ((ot, ((fst(fst(fst(fst(x))))), ((map (uncurry mmm))(snd(snd(fst(fst(fst(x))))))))), (snd(fst(x)))))); read_cap_mchar :: Char -> Mchar; read_cap_mchar c = (read_mchar (toLower c)); read_points :: Char -> Maybe(Points); read_points c = (case c of { ('.')-> Nothing; (_)-> (Just (case (isUpper c) of { (True)-> points_blank; (_)-> (get_points c) })) }); read_board_state :: [](String) -> Board_state; read_board_state ss = (make_board_state (mkboard read_cap_mchar ss) (mkboard read_points ss)); type Input_state = ((Tiles_you_have, Board_state), Tiles_you_have); parse_state_mine :: [](String) -> Input_state; parse_state_mine ss = (let { b :: [](String); b = (const ((map tail)(init(ss))) "drop first character cuz emacs numbers columns from 0"); ll :: [](String); ll = (words(last(ss))); blanks_i_have :: Int; blanks_i_have = (read ((!!) ll 0)); letters_i_have :: String; letters_i_have = ((!!) ll 1) } in (((blanks_i_have, (mkarray letters_i_have)), (read_board_state b)), (read_used_tiles b blanks_i_have letters_i_have))); template :: IO(()); template = (putStr(unlines((replicate 15)(((:) (head " "))((replicate 15)(head("."))))))); do_move :: IO(()); do_move = (getContents >>= (putStr . show_list . (map format_1) . (zipWith (let { make :: Int -> Essence_of_play -> (Essence_of_play, Points); make i e = (e, (Points i)) } in make) (enumFrom 1)) . (sortBy (comparing fst)) . (uncurry unsorted_plays) . fst . parse_state_mine . lines)); tiles_array_to_list :: Tiles_you_have -> [](Mchar); tiles_array_to_list t = ((++) (replicate (fst t) Nothing) ((map Just)(concat((map(uncurry(flip(replicate))))(assocs(snd(t))))))); tile_list_to_array :: [](Mchar) -> Tiles_you_have; tile_list_to_array t = ((length((filter isNothing)(t))), (mkarray(filter_justs(t)))); run_using_available_tiles :: [](Mchar) -> [](Mchar) -> Board_state -> ([](Mchar), (Points, Board_state)); run_using_available_tiles available old_current_tiles b = (let { num_to_grab :: Int; num_to_grab = ((-) max_tiles (length old_current_tiles)); grab_and_leftover :: ([](Mchar), [](Mchar)); grab_and_leftover = (splitAt num_to_grab available); new_current_tiles :: Tiles_you_have; new_current_tiles = (tile_list_to_array(((++) old_current_tiles)(fst(grab_and_leftover)))); best_play :: (Essence_of_play, Board_state); best_play = (best_single_play new_current_tiles b) } in ((snd grab_and_leftover), ((fst (fst best_play)), (snd best_play)))); two_ply_points :: [](Mchar) -> Board_state -> [](Mchar) -> (Points, Points); two_ply_points my_tiles b available = (let { opponent_move :: ([](Mchar), (Points, Board_state)); opponent_move = (run_using_available_tiles available [] b); my_move :: Points; my_move = (fst(snd((run_using_available_tiles (fst opponent_move) my_tiles (snd (snd opponent_move)))))) } in (my_move, (fst(snd(opponent_move))))); num_samples :: Int; num_samples = 100; num_to_process :: Int; num_to_process = 8; sample_two_plies_io :: Tiles_you_have -> Board_state -> Points -> [](Mchar) -> IO(Points); sample_two_plies_io my_tiles b my_points available_tiles = (do{ r_available :: []([](Mchar)) <- (sequence((replicate num_samples)(random_permutation_IO(available_tiles)))); (let { answer :: []((Points, Points)); answer = (((flip using) (parList rnf))((map (two_ply_points (tiles_array_to_list my_tiles) b))(r_available))) } in (do{ (return(sum((map ((+) my_points))((map (uncurry (-)))(answer))))); })); }); call_sample_two_plies_io :: [](Mchar) -> Board_state -> Essence_of_play -> IO(Points); call_sample_two_plies_io available_tiles old e = (sample_two_plies_io (snd(snd(e))) (update_board_state old e) (fst(e)) available_tiles); print_sample_two_plies :: [](Mchar) -> Board_state -> Essence_of_play -> IO(()); print_sample_two_plies available_tiles old e = (do{ answer :: Points <- (call_sample_two_plies_io available_tiles old e); (putStrLn(show(format_1((e, answer))))); }); read_available_char :: Char -> [](Mchar); read_available_char c = (case c of { ('.')-> []; (_)-> (case (isUpper c) of { (True)-> (return Nothing); (_)-> (return(Just(c))) }) }); read_used_tiles :: [](String) -> Int -> String -> Tiles_you_have; read_used_tiles ss blanks_i_have letters_i_have = (let { parse_board :: [](Mchar); parse_board = ((concatMap read_available_char)(concat(ss))) } in (((+) (length((filter isNothing)(parse_board))) blanks_i_have), (mkarray ((++) (filter_justs parse_board) letters_i_have)))); total_tiles :: Tiles_you_have; total_tiles = (2, (mkarray ("aaaaaaaaab" ++ "bccddddeee" ++ "eeeeeeeeef" ++ "fggghhiiii" ++ "iiiiijklll" ++ "lmmnnnnnno" ++ "oooooooppq" ++ "rrrrrrssss" ++ "ttttttuuuu" ++ "vvwwxyyz"))); subtract_tiles :: Tiles_you_have -> Tiles_you_have -> Tiles_you_have; subtract_tiles big small = (((-) (fst big) (fst small)), (listArray alphabet (zipWith (-) (elems (snd big)) (elems (snd small))))); do_move_2 :: ([](Essence_of_play) -> [](Essence_of_play)) -> IO(()); do_move_2 play_sorter = (do{ (hSetBuffering stdout LineBuffering); (setStdGen (mkStdGen 1)); (putStrLn rcs_code); (putStrLn ("num-samples " ++ (show num_samples))); fi :: String <- getContents; (let { available_tiles :: [](Mchar); available_tiles = (tiles_array_to_list (subtract_tiles total_tiles (snd(state_mine)))); current_tiles :: Tiles_you_have; current_tiles = (fst(fst(state_mine))); state_mine :: Input_state; state_mine = (parse_state_mine (lines fi)); bs :: Board_state; bs = (snd(fst(state_mine))); plays :: [](Essence_of_play); plays = (play_sorter((unsorted_plays current_tiles bs))) } in (do{ (putStrLn(last(lines(fi)))); (putStrLn(show(length(plays)))); (mapM_ (print_sample_two_plies available_tiles bs) plays); })); }); format_1 :: (Essence_of_play, Points) -> (Points, Points, Ot((Coord, String))); format_1 x = ((snd(x)), (fst(fst(x))), (format_2(fst(snd(fst(x)))))); format_2 :: Essence_of_play_1 -> Ot((Coord, String)); format_2 x = ((fst x), (((fst(snd(x))), (fst(head(snd(snd(x)))))), ((map (uncurry format_letter))((map snd)(snd(snd(x))))))); format_letter :: Char -> Points -> Char; format_letter c p = (case p of { (Points(0))-> (toUpper c); (_)-> c }); no_blank_play :: Essence_of_play -> Bool; no_blank_play e = (and((map (((/=) (Points 0)) . snd . snd))(snd(snd(fst(snd(e))))))); a_play_sorter :: Int -> [](Essence_of_play) -> [](Essence_of_play); a_play_sorter skip plays = ((take num_to_process)((drop skip)((sortBy (comparing fst))(plays)))); go :: Int -> IO(()); go skip = (do_move_2 (a_play_sorter skip)); no_blank :: Int -> IO(()); no_blank skip = (do_move_2 (no_blank_sorter skip)); no_blank_sorter :: Int -> [](Essence_of_play) -> [](Essence_of_play); no_blank_sorter skip plays = ((take num_to_process)((sortBy (comparing fst))((drop skip)((filter no_blank_play)(plays))))) }