{-
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)))))
}