Haskell を使用して Paper Scissors Stone のゲームを作成し、理解を深めようとしています。
残念ながら、以下のソース コードは望ましくない回答を提供します。
例えば:
>play pStone pRandom 1
1 games were played. Player 1 won 1 and player 2 won 1, making the match a draw.
1 ゲームをプレイした場合、1 勝か 0 勝しかないはずです。
>play pStone pCopy 100
100 games were played. Player 1 won 1 and player 2 won 1, making the match a draw.
100 ゲームがプレイされ、(最初のラウンドの後) 両方が同じ手でプレイされた場合、1 勝または 0 勝のいずれかしかないはずです。
>play pCopy pAntiCopy 100
100 games were played. Player 1 won 31 and player 2 won 37, making player 2 the overall winner.
pCopy と pAntiCopy の意図した定義により、pAntiCopy は 99 または 100 のいずれかを獲得し、pCopy は 0 または 1 を獲得する必要があります。
この動作の最も可能性の高い理由は、乱数が最後に評価されるためだと思います。つまり、同じ乱数に依存するはずの 2 つの値が、2 つの別々の乱数に依存することになります。上記で正しいですか?
私が正しければ、これをどのように修正すればよいか教えていただけますか? 間違っている場合は、何が問題で、どのように解決すればよいか教えていただけますか?
乱数のリストを生成してからそれらを使用し、メイン関数から関連する関数に引数として渡すことを提案する別の問題の解決策を読みました。ただし、必要な乱数の数は、使用されている計画に応じて 0 から 2*numRounds の範囲になる可能性があるため (これが機能している場合は、より高度な計画を作成するつもりです)、コードの可読性はさらに低下します。
私は Haskell と関数型プログラミング全般に不慣れなので、以下のソース コードのスタイルについてお詫び申し上げます。それを改善する方法について何か提案があれば、それも大歓迎です。
import System.Random
data Move= Paper|Scissors|Stone deriving Show
type Plan=([IO Move]->[IO Move]->IO Move)
play :: Plan -> Plan -> Integer -> IO ()
play plan1 plan2 numRounds=do p1intwins<-p1wins;p2intwins<-p2wins;putStr(show numRounds ++ " games were played. Player 1 won " ++ show p1intwins ++ " and player 2 won " ++ show p2intwins ++ ", making " ++ (if p1intwins > p2intwins then "player 1 the overall winner." else (if p1intwins < p2intwins then "player 2 the overall winner." else "the match a draw."))) where (_, _, _, _, _, _, p1wins, p2wins)=(playRound (plan1, plan2, numRounds,[],[], 0, return 0, return 0))
playRound :: (Plan, Plan, Integer, [IO Move], [IO Move], Integer, IO Integer, IO Integer) -> (Plan, Plan, Integer, [IO Move], [IO Move], Integer, IO Integer, IO Integer)
playRound (plan1, plan2, numRounds, p1moves, p2moves, elapsedRounds, p1wins, p2wins)=if elapsedRounds==numRounds then (plan1, plan2, numRounds, p1moves, p2moves, elapsedRounds, p1wins, p2wins) else (playRound (plan1, plan2, numRounds, p1moves++[p1move], p2moves++[p2move], elapsedRounds+1, do p1win<-beatsCaller p1move p2move;p1intwins<-p1wins;return (p1intwins+if p1win then 1 else 0), do p2win<-beatsCaller p2move p1move;p2intwins<-p2wins;return(p2intwins+(if p2win then 1 else 0)) )) where p1move=plan1 p1moves p2moves; p2move=plan2 p2moves p1moves
beatsCaller :: IO Move -> IO Move -> IO Bool
beatsCaller iom1 iom2=do m1<-iom1;m2<-iom2;return(beats m1 m2)
beats :: Move -> Move -> Bool
beats Scissors Paper=True
beats Stone Scissors=True
beats Paper Stone=True
beats _ _=False
-- ###############Plans###################
pStone :: Plan
pStone _ _ = return Stone
pScissors :: Plan
pScissors _ _ = return Scissors
pPaper :: Plan
pPaper _ _ = return Paper
pUScissors :: Plan
pUScissors [] _ = randomMove
pUScissors _ _ = return Scissors
pCopy :: Plan
pCopy _ []= randomMove
pCopy _ theirMoves= last theirMoves
pRandom :: Plan
pRandom _ _=randomMove
pAntiCopy :: Plan
pAntiCopy [] _ = randomMove
pAntiCopy ourMoves _ = do ourMove<-last ourMoves;return(beaterOf ourMove)
-- ##############Plan Logic###############
beaterOf :: Move -> Move
beaterOf Scissors = Stone
beaterOf Paper = Scissors
beaterOf Stone = Paper
randomMove :: IO Move
randomMove=do x<-genRandom;return (doRMove x)
doRMove:: Int -> Move
doRMove rand
|rand==1 =Paper
|rand==2 =Scissors
|rand==3 =Stone
genRandom :: IO Int
genRandom =getStdRandom (randomR (1,3))