2013年8月16日金曜日

Haskellの練習:ヒットアンドブローキラー

import Data.Char
import Data.List
import Control.Applicative
import System.Random

main = do
gen <- getStdGen
let (x, newgen) = randomR (0, length list -1) gen
let firstStr = list !! x
putStrLn $ "HEY. FISRTLY, JUST SAY "++ firstStr ++ "."
solver list [] firstStr newgen

addZero :: String -> String
addZero str = replicate (4 - length str) '0' ++ str

list = filter (\ x -> length x > 3) $ map (nub) $ nub $ map (addZero.nub.addZero.show) [1..9999]

hitCount :: String -> String -> Int
hitCount ans datni = length $ filter (&&True) $ zipWith (\ a b -> if(a==b) then True else False) ans datni

blowCount :: String -> String -> Int
blowCount ans datni = length (filter (&&True) $ (==) <$> ans <*> datni ) - (hitCount ans datni)

behead :: [String] -> [[String]] -> [String]
behead str datni = filter (\ cand -> foldl (\ truth [a,b,c] -> truth && (hitCount a cand) == read b && (blowCount a cand) == read c ) True datni) str

solver :: (RandomGen g) => [String] -> [[String]] -> String -> g -> IO ()
solver cand datni str gen = do
putStrLn $ "GIVE ME INFORMATION: HIT, BLOW..."
element <- fmap (words) getLine
let newdatni = (str : element) : datni
let newcand = behead cand newdatni
let (nextNumber , newgen) = randomR (0, length newcand -1) gen
let nextStr = newcand !! nextNumber
putStrLn $ "NOW, THE NUMBER OF CANDIDATES ARE "++ (show (length newcand )) ++ "."
putStrLn $ "HEY, YOU JUST SAY " ++ nextStr ++ "."
if (length newcand == 1)
then return ()
else solver newcand newdatni nextStr newgen


Haskellの練習がてら、以前にjavaでつくったヒットアンドブローを解くプログラムコードをHaskellでも書いてみた。javaのソースコードが209行、Haskellのソースコードが39行。テキストサイズだと、javaが4918byte, Haskellが1555byteですね。こんな短く書けるなんて…(いや俺がjavaが下手なだけかもしれない)。
テキストはすごいH本を使っています。まあ他にも色んなサイトを転々とはしましたが。
javaでもリスト処理をもう少しまともに勉強すれば短くなったのかもしれない。
Haskellのリスト処理は簡単で素晴らしいですね。

短くできそうなところは…
・list、[1...9999]からヒットアンドブローに使う重複しない4桁の数字を抽出するところ。
・behead (候補を落としていく関数) のラムダ式の入れ子
かなあと思います。とりあえず今の知識だけで頑張りました。


0 件のコメント:

コメントを投稿