Best Practices for Where clauses - haskell
I wrote a simple tic-tac-toe program in Haskell. It runs on the command line, has a one and two player mode, and implements a minimax algorithm when you play against it.
I'm used to writing proper code in OO languages, but Haskell is new to me. This code works reasonably well, but seems hard to read (even to me!). Any suggestions on how to make this code more...Haskellian?
import Data.List
import Data.Char
import Data.Maybe
import Control.Monad
data Square = A | B | C | D | E | F | G | H | I | X | O deriving (Read, Eq, Ord)
instance Show Square where
show A = "a"
show B = "b"
show C = "c"
show D = "d"
show E = "e"
show F = "f"
show G = "g"
show H = "h"
show I = "i"
show X = "X"
show O = "O"
type Row = [Square]
type Board = [Row]
data Player = PX | PO deriving (Read, Eq)
instance Show Player where
show PX = "Player X"
show PO = "Player O"
data Result = XWin | Tie | OWin deriving (Read, Show, Eq, Ord)
main :: IO ()
main = do
putStrLn "Let's play some tic tac toe!!!"
putStrLn "Yeeeaaaaaahh!!!"
gameSelect
gameSelect :: IO ()
gameSelect = do
putStrLn "Who gonna play, one playa or two??? (Enter 1 or 2)"
gameMode <- getLine
case gameMode of "1" -> onePlayerMode
"2" -> twoPlayerMode
gameMode -> gameSelect
where onePlayerMode = do
putStrLn "One playa"
putStrLn "Cool! Get ready to play...AGAINST MY INVINCIBLE TIC TAC TOE AI!!!!! HAHAHAHA!!!"
gameLoop 1 emptyBoard PX
twoPlayerMode = do
putStrLn "Two players"
gameLoop 2 emptyBoard PX
emptyBoard = [[A,B,C],[D,E,F],[G,H,I]]
gameLoop :: Int -> Board -> Player -> IO ()
gameLoop noOfPlayers board player = do
case detectWin board of Just XWin -> endgame board XWin
Just OWin -> endgame board OWin
Just Tie -> endgame board Tie
Nothing -> if noOfPlayers == 1
then if player == PX
then enterMove 1 board player
else enterBestMove board PO
else enterMove 2 board player
enterMove :: Int -> Board -> Player -> IO ()
enterMove noOfPlayers board player = do
displayBoard board
if noOfPlayers == 1
then do putStrLn ("Make your move. (A-I)")
else do putStrLn (show player ++ ", it's your turn. (A-I)")
move <- getLine
print move
if not $ move `elem` ["a","b","c","d","e","f","g","h","i"]
then do
putStrLn $ move ++ " is not a move, doofus"
gameLoop noOfPlayers board player
else if (read (map toUpper move) :: Square) `elem` [ sq | sq <- concat board]
then do
gameLoop noOfPlayers (newBoard (read (map toUpper move) :: Square) player board) (if player == PX then PO else PX)
else do
putStrLn "That square is already occupied"
gameLoop noOfPlayers board player
enterBestMove :: Board -> Player -> IO ()
enterBestMove board player = gameLoop 1 (newBoard bestmove player board) PX
where bestmove = fst $ findBestMove PO board
findBestMove :: Player -> Board -> (Square, Result)
findBestMove player board
| player == PO = findMax results
| player == PX = findMin results
where findMin = foldl1 (\ acc x -> if snd x < snd acc then x else acc)
findMax = foldl1 (\ acc x -> if snd x > snd acc then x else acc)
results = [ (sq, getResult b) | (sq, b) <- boards player board ]
getResult b = if detectWin b == Nothing
then snd (findBestMove (if player == PX then PO else PX) b)
else fromJust $ detectWin b
boards :: Player -> Board -> [(Square, Board)]
boards player board = [(sq, newBoard sq player board) | sq <- concat board, sq /= X, sq /=O]
displayBoard :: Board -> IO ()
displayBoard board = do
mapM_ print board
newBoard :: Square -> Player -> Board -> Board
newBoard move player board = [ [if sq == move then mark else sq | sq <- row] | row <- board]
where mark = if player == PX then X else O
detectWin :: Board -> (Maybe Result)
detectWin board
| [X,X,X] `elem` board ++ transpose board = Just XWin
| [X,X,X] `elem` [diagonal1 board, diagonal2 board] = Just XWin
| [O,O,O] `elem` board ++ transpose board = Just OWin
| [O,O,O] `elem` [diagonal1 board, diagonal2 board] = Just OWin
| [X,X,X,X,X,O,O,O,O] == (sort $ concat board) = Just Tie
| otherwise = Nothing
where
diagonal1 :: Board -> [Square]
diagonal1 bs = bs!!0!!0 : bs!!1!!1 : bs!!2!!2 : []
diagonal2 :: Board -> [Square]
diagonal2 bs = bs!!0!!2 : bs!!1!!1 : bs!!2!!0 : []
endgame :: Board -> Result -> IO ()
endgame board result = do
displayBoard board
if result `elem` [XWin, OWin]
then
let player = if result == XWin then PX else PO
in do
putStrLn ("The game is over, and " ++ show player ++ " wins!")
putStrLn ((if player == PX then show PO else show PX) ++ " is a loser lol")
else do
putStrLn "The game is a tie"
putStrLn "You are both losers! Ugh!"
putStrLn "Want to play again? (y/n)"
again <- getLine
if again `elem` ["y", "Y", "yes", "Yes", "YES"]
then gameSelect
else do
putStrLn "Goodbye"
EDIT: with special thanks to #Chi and #Caridorc, I've made the following changes. Further suggestions will be considered and updated as well
import Data.List
import Data.Char
import Data.Maybe
import Control.Monad
data Square = A | B | C | D | E | F | G | H | I | X | O deriving (Read, Eq, Ord)
instance Show Square where
show A = "a"
show B = "b"
show C = "c"
show D = "d"
show E = "e"
show F = "f"
show G = "g"
show H = "h"
show I = "i"
show X = "X"
show O = "O"
type Row = [Square]
type Board = [Row]
data Player = PX | PO deriving (Read, Eq)
instance Show Player where
show PX = "Player X"
show PO = "Player O"
data Result = XWin | Tie | OWin deriving (Read, Show, Eq, Ord)
main :: IO ()
main = do
putStrLn "Let's play some tic tac toe!!!"
putStrLn "Yeeeaaaaaahh!!!"
gameSelect
gameSelect :: IO ()
gameSelect = do
putStrLn "Who gonna play, one playa or two??? (Enter 1 or 2)"
gameMode <- getLine
case gameMode of
"1" -> onePlayerMode
"2" -> twoPlayerMode
_ -> gameSelect
where onePlayerMode = do
putStrLn "One playa"
putStrLn "Cool! Get ready to play...AGAINST MY INVINCIBLE TIC TAC TOE AI!!!!! HAHAHAHA!!!"
gameLoop 1 emptyBoard PX
twoPlayerMode = do
putStrLn "Two players"
gameLoop 2 emptyBoard PX
emptyBoard = [[A,B,C],[D,E,F],[G,H,I]]
displayBoard :: Board -> IO ()
displayBoard board = do
mapM_ print board
otherPlayer :: Player -> Player
otherPlayer PX = PO
otherPlayer PO = PX
gameLoop :: Int -> Board -> Player -> IO ()
gameLoop noOfPlayers board player = do
case detectWin board of
Just res -> endgame board res
Nothing -> case noOfPlayers of
1 -> case player of
PX -> enterMove 1 board player
PO -> enterBestMove board PO
2 -> enterMove 2 board player
enterMove :: Int -> Board -> Player -> IO ()
enterMove noOfPlayers board player = do
displayBoard board
case noOfPlayers of
1 -> do putStrLn ("Make your move. (A-I)")
2 -> do putStrLn (show player ++ ", it's your turn. (A-I)")
move <- getLine
print move
if not $ move `elem` ["a","b","c","d","e","f","g","h","i"] then do
putStrLn $ move ++ " is not a move, doofus"
gameLoop noOfPlayers board player
else if (read (map toUpper move) :: Square) `elem` (concat board) then do
gameLoop noOfPlayers (newBoard (read (map toUpper move) :: Square) player board) (otherPlayer player)
else do
putStrLn "That square is already occupied"
gameLoop noOfPlayers board player
enterBestMove :: Board -> Player -> IO ()
enterBestMove board player = gameLoop 1 (newBoard bestmove player board) PX
where bestmove = fst $ findBestMove PO board
findBestMove :: Player -> Board -> (Square, Result) -- minimax algorithm
findBestMove player board
| player == PO = findMax results
| player == PX = findMin results
where findMin = foldl1 (\ acc x -> if snd x < snd acc then x else acc)
findMax = foldl1 (\ acc x -> if snd x > snd acc then x else acc)
results = [ (sq, getResult b) | (sq, b) <- boards player board ]
getResult b = case detectWin b of
Nothing -> snd (findBestMove (otherPlayer player) b)
Just x -> x
boards :: Player -> Board -> [(Square, Board)]
boards player board = [(sq, newBoard sq player board) | sq <- concat board, sq /= X, sq /=O]
newBoard :: Square -> Player -> Board -> Board
newBoard move player board = [ [if sq == move then mark else sq | sq <- row] | row <- board]
where mark = if player == PX then X else O
detectWin :: Board -> (Maybe Result)
detectWin board
| [X,X,X] `elem` (triplets board) = Just XWin
| [O,O,O] `elem` (triplets board) = Just OWin
| [X,X,X,X,X,O,O,O,O] == (sort $ concat board) = Just Tie
| otherwise = Nothing
triplets :: Board -> [[Square]]
triplets board = board ++ transpose board ++ [diagonal1] ++ [diagonal2]
where
flat = concat board
diagonal1 = [flat !! 0, flat !! 4, flat !! 8]
diagonal2 = [flat !! 2, flat !! 4, flat !! 6]
endgame :: Board -> Result -> IO ()
endgame board result = do
displayBoard board
putStrLn $ endGameMessage result
putStrLn "Want to play again? (y/n)"
again <- getLine
if again `elem` ["y", "Y", "yes", "Yes", "YES"]
then gameSelect
else do
putStrLn "Goodbye"
endGameMessage :: Result -> String
endGameMessage result
| result `elem` [XWin, OWin] = winnerNotice ++ loserNotice
| otherwise = "The game is a tie\n" ++ "You are both losers! Ugh!"
where
winner = case result of
XWin -> PX
OWin -> PO
winnerNotice = "The game is over, and " ++ show winner ++ " wins!\n"
loserNotice = (show $ otherPlayer winner) ++ " is a loser lol"
Code style is often a matter of personal preference, in Haskell arguably more than in other languages with a "standard" style guide. Still, here's a few random suggestions.
Don't over-indent cases: just use another line
case gameMode of "1" -> onePlayerMode
"2" -> twoPlayerMode
gameMode -> gameSelect
vs
case gameMode of
"1" -> onePlayerMode
"2" -> twoPlayerMode
gameMode -> gameSelect
or even
case gameMode of
"1" -> onePlayerMode
"2" -> twoPlayerMode
_ -> gameSelect
case is usually preferred to if .. == Constructor:
if player == PX
then enterMove 1 board player
else enterBestMove board PO
vs
case player of
PX -> enterMove 1 board player
PY -> enterBestMove board PO
I'd strongly recommend against using partial functions like fromJust, since they can crash your program if you forget to check for Nothing beforehand. Safer alternatives exist, which never cause such crashes -- less burden on the programmer.
if detectWin b == Nothing
then snd (findBestMove (if player == PX then PO else PX) b)
else fromJust $ detectWin b
vs
case detectWin b of
Nothing -> snd $ findBestMove (if player == PX then PO else PX) b
Just x -> x
or
fromMaybe (snd $ findBestMove (if player == PX then PO else PX) b)
$ detectWin b
Try to factorize commonly used functions. For instance
nextPlayer PX = PO
nextPlayer PO = PX
can replace uses of
if player == PX then PO else PX
No do is needed when there's only one statement:
if noOfPlayers == 1
then do putStrLn ("Make your move. (A-I)") -- no need for parentheses here
else do putStrLn (show player ++ ", it's your turn. (A-I)")
Since you mention where in the title, let me state that I have mixed feelings about where, in general. I know I often tend to avoid where in favor of let, but this feeling is not shared with many other Haskellers, so take this with some care.
Personally, I tend to limit my where uses to one-liners:
foo = f x y
where x = ...
y = ...
Especially in do blocks, which might span several lines, I prefer lets:
foo = do
line
line using x -- what is x ??!?
line
...
line
where x = ... -- ah, here it is
vs
foo = do
line
let x = ...
line using x
line
...
line
However, feel free to adopt the style you find more readable.
Also don't forget to add a few comments, as #mawalker points out. Some definitions are obvious and don't need any explanation. Others could benefit from a few lines explaining the purpose.
Related
Haskell make recursion for Chars in String
I want to create game Magic 15 Puzzle in Haskell I have function set :: [[Char]] -> Char -> [[Char]] . It switches Char with empty space in [[Char]]. *Main> pp puzzle2 AC DE FBHIJ KGLNO PQMRS UVWXT *Main> pp (set puzzle2 'C') A CDE FBHIJ KGLNO PQMRS UVWXT *Main> Now I want to do recursion for [Char] (or String) like this (To do set xs for previous set x) puzzle :: Result -> [Char] -> Result puzzle gameboard (x:xs) = set (set (x:xs) x) xs But compilation says it is error: Couldn't match expected type ‘Char’ with actual type ‘[Char]’ I expect this output: *Main> pp(puzzle puzzle2 "CB") ABCDE F HIJ KGLNO PQMRS UVWXT What can I do to solve this? Thanks a lot in advance for answer! Whole Code: import Data.Char type Result = [String] pp :: Result -> IO () pp x = putStr (concat (map (++"\n") x)) puzzle2 :: [[Char]] puzzle2 = ["AC DE", "FBHIJ", "KGLNO", "PQMRS", "UVWXT"] getCords board x = head ( head [[(row_index, column_index) |(column_index, char) <- zip[1..] row, x == char] |(row_index,row)<- zip [1..]board,x `elem` row]) getRow board c = fst ( getCords board c) getCol board c = snd ( getCords board c) check ch1 ch2 board = (getRow board ch2 == getRow board ch1 + 1 || getRow board ch2 == getRow board ch1 - 1) && (getCol board ch1 == getCol board ch2) || ((getRow board ch1 == getRow board ch2) && (getCol board ch2 == getCol board ch1 + 1 || getCol board ch2 == getCol board ch1 - 1) ) set gameboard x | check x ' ' gameboard = [[if ch == ' ' then x else if ch == x then ' ' else ch | ch<- line] | line<-gameboard] | not (check x ' ' gameboard ) = [[ch | ch<- line] | line<-gameboard] puzzle :: Result -> [Char] -> Result puzzle gameboard (x:xs) = set (set (x:xs) x) xs
Just change the last function to puzzle :: Result -> [Char] -> Result puzzle g [] = g puzzle g (x:xs) = puzzle (set g x) xs
Change user input to input "Char" not a "Value"?
This is my code module Main where import Control.Monad (mapM) import Text.Read (readMaybe) import System.IO (BufferMode(..), stdout, hSetBuffering) mouth = [('P',0),('(',1),('[',2),(')',3),('O',4)] eyes = [(':',1),('8',2),(';',3)] findKey :: (Eq k) => k -> [(k,v)] -> Maybe v findKey key = foldr (\(k,v) acc -> if key == k then Just v else acc) Nothing query :: Read a => String -> IO a query prompt = do putStr $ prompt ++ ": " val <- readMaybe <$> getLine case val of Nothing -> do putStrLn "Sorry that's a wrong value - please reenter" query prompt Just v -> return v ngoers :: IO Int ngoers = query "Enter the number of Concertgoers" cgoers :: Int -> IO (Int, Double) cgoers i = do c <- query prompt return (fromIntegral i,c) where prompt = "Enter the emoticon for concertgoer " ++ show (i+1) concertgoer :: IO [(Int, Double)] concertgoer = do n <- ngoers mapM cgoers [0,1..n-1] presentResult :: Double -> IO () presentResult v = putStrLn $ "The results are: " ++ show v main :: IO () main = do p <- concertgoer presentResult $ 0 I want this output Enter the number of Concertgoers: 4 Enter the emoticon for concertgoer 1: :( Enter the emoticon for concertgoer 2: :) Enter the emoticon for concertgoer 3: ;P Enter the emoticon for concertgoer 4: ;o The results are: 2 4 3 7
From your example I'm guessing that you match each eye and mouth to a number, and a emoticon is the sum if those... but you haven't explained nothing of this in your post. Assuming so, this is a very naive way to write It import Control.Monad (mapM) -- Define the data you want to use data Eye = Normal | Glasses | Wink deriving(Show, Eq) data Mouth = P | Sad | Bracket | Happy | O deriving(Show, Eq) data Face = Face Eye Mouth deriving(Show, Eq) -- Define special readers and elemToInt readEyes :: Char -> Maybe Eye readEyes c = case c of ':' -> Just Normal '8' -> Just Glasses ';' -> Just Wink _ -> Nothing -- This is equivalent to derive Enum class and apply fromEnum. Try to do it your self ;) eyeToInt :: Eye -> Int eyeToInt Normal = 1 eyeToInt Glasses = 2 eyeToInt Wink = 3 readMouth :: Char -> Maybe Mouth readMouth c = case c of 'P' -> Just P '(' -> Just Sad '[' -> Just Bracket ')' -> Just Happy 'O' -> Just O _ -> Nothing mouthToInt :: Mouth -> Int mouthToInt P = 0 mouthToInt Sad = 1 mouthToInt Bracket = 2 mouthToInt Happy = 3 mouthToInt O = 4 readFace :: String -> Maybe Face readFace [] = Nothing readFace [e,m] = do eye <- readEyes e mouth <- readMouth m return $ Face eye mouth readFace _ = Nothing faceToInt :: Face -> Int faceToInt (Face e m) = eyeToInt e + mouthToInt m -- The main loop is straight forward main :: IO () main = do putStrLn "Enter the number of Concertgoers" number <- read <$> getLine -- Use safe reading better... I am using an online repl so no access to it results <- mapM getEmoticon [1..number] putStrLn $ "The results are: " ++ show results where getEmoticon n = do putStrLn $ "Enter the emoticon for concertgoer " ++ show n face <- readFace <$> getLine case face of Nothing -> do putStrLn "That's not an emotion!!" getEmoticon n Just f -> return $ faceToInt f I think It is what you expect but let me know
No instance for (Num String) arising from the literal `1' in if
Consider the next piece of code - pvp :: Board -> Int -> IO () pvp board player = do playerchoice <- prompt $ ("Player " ++ (show (player + 1)) ++ ", it's your turn:") let newboard = if player == 0 then put board X (read playerchoice) else put board O (read playerchoice) case newboard of Nothing -> do putStrLn "Invalid move." pvp board player Just board' -> putStrLn "Valid move." When i try to compile the script i get the following error- No instance for (Num String) arising from the literal 1' In the second argument of (==)', namely 1' This is how i call pvp - main = do playGame emptyBoard where playGame board = do game_choise <- prompt "Choose game type: (1) PvC (2) PvP" if game_choise == 1 then putStrLn "1" else pvp board 0
my most likely fix (guess) would be main = do playGame emptyBoard where playGame board = do game_choise <- prompt "Choose game type: (1) PvC (2) PvP" if read game_choise == (1 :: Int) then putStrLn "1" else pvp board 0
Updating visibility of dynamically created content
Duplicating this from github as per #HeinrichApfelmus's suggestion: This may be just a usage error on my part, but I am noticing a strange phenomenon when trying to set up conditional visibility/layout for dynamically created UI elements (in WX of course). As somewhat of a toy-example, I tried to create a widget that created StaticText elements on the fly and allowed the user to "browse" through these elements through '<' '>' buttons. The problem I am noting is that all labels are invisible until a new one is created, at which point the current widget in focus becomes visible. Whether this is a bug or just a paradigm I am misusing, or a subtlety with reactive frameworks, I am unsure as to how to resolve this. Here is the code I have at this point, which exhibits the problem: {-# LANGUAGE RecursiveDo #-} module Test.Adder where import Reactive.Banana import Reactive.Banana.WX import Graphics.UI.WX.Attributes import Graphics.UI.WX hiding (Event, newEvent, empty, Identity) import Graphics.UI.WXCore hiding (Event, Timer, empty, Identity, newEvent) import Graphics.UI.WXCore.Frame -- | Combine Unit-Events anyEvent :: [Event ()] -> Event () anyEvent = foldl1 (unionWith (\_ _ -> ())) -- | Unsugared if-then-else function if_ :: Bool -> a -> a -> a if_ True x _ = x if_ False _ y = y -- | Apply a function to the value at an index, or return a default value -- if the index is out of range (!?) :: (a -> b) -> b -> Int -> ([a] -> b) (f!? ~y) n xs | n < 0 = y | otherwise = case drop n xs of x:_ -> f x [] -> y main :: IO () main = start test create :: Window w -> Int -> Behavior Int -> Event Int -> Event () -> MomentIO (StaticText ()) create t i bi ei eRef = do let tx = replicate i '\t' ++ show i x <- liftIO $ staticText t [ text := tx ] let beq = (==i) <$> bi let eMe = filterE (==i) ei sink x [ visible :== beq ] reactimate (refresh x <$ anyEvent [ eRef, () <$ eMe ]) return x test :: IO () test = do f <- frame [text := "Test"] add <- button f [ text := "+" ] prv <- button f [ text := "<" ] cur <- staticText f [] nxt <- button f [ text := ">" ] tab <- panel f [ clientSize := sz 200 300 ] deb <- staticText f [] ref <- button f [ text := "refresh" ] let networkDescription :: MomentIO () networkDescription = mdo eAdd <- event0 add command eRef <- event0 ref command let bNotFirst = (>0) <$> bCur bNotLast = (<) <$> bCur <*> bNext sink prv [ enabled :== bNotFirst ] sink cur [ text :== show <$> bCur ] sink nxt [ enabled :== bNotLast ] ePrev <- event0 prv command eNext <- event0 nxt command let eDelta :: Enum n => Event (n -> n) eDelta = unions [ pred <$ whenE bNotFirst ePrev , succ <$ whenE bNotLast eNext ] eChange = flip ($) <$> bCur <#> eDelta bCur <- stepper 0 $ eChange (eIndex, bCount) <- mapAccum 0 ((\x -> (x, succ x)) <$ eAdd) let bView = (\n i -> if_ (n==0) (0) i) <$> bCount <*> bCur bNext = pred <$> bCount eCreate = (\n -> create tab n bView eChange $ anyEvent [eRef,eAdd]) <$> eIndex reCreate <- execute eCreate bItemer <- accumB id $ flip (.) . (:) <$> reCreate let bItems = ($[]) <$> bItemer bThis = (widget!?(nullLayouts!!0)) <$> bCur <*> bItems sink tab [ layout :== bThis ] liftIO $ set f [ layout := column 5 [ margin 10 $ row 5 [ widget add , widget prv , widget cur , widget nxt , widget ref ] , fill $ widget tab ] ] network <- compile networkDescription actuate network >
Haskell checkers - how to write a function that returns a list of possible jumps
I would like to ask for help because I do not know how to write a function that analyzes possible jumps for a checkers' pawn. I am stuck and I will be very thankful for help. I created a chessboard and a list of tuples that represent chessboard as a list. This is the form to be able to show the chessboard on the screen: "\"p.p.p.p.\n.p.p.p.p\np.p.p.p.\n........\n........\n........\nP.P.P.P.\n.P.P.P.P\nP.P.P.P.\n\"" And this is the form which I use to analyze possible moves of pieces: [((1,1),'p'),((1,2),'.'),((1,3),'p'),((1,4),'.'),((1,5),'p'),((1,6),'.'),((1,7),'p'),((1,8),'.'),((2,1),'.'),((2,2),'p'),((2,3),'.'),((2,4),'p'),((2,5),'.'),((2,6),'p'),((2,7),'.'),((2,8),'p'),((3,1),'p'),((3,2),'.'),((3,3),'p'),((3,4),'.'),((3,5),'p'),((3,6),'.'),((3,7),'p'),((3,8),'.'),((4,1),'.'),((4,2),'.'),((4,3),'.'),((4,4),'.'),((4,5),'.'),((4,6),'.'),((4,7),'.'),((4,8),'.'),((5,1),'.'),((5,2),'.'),((5,3),'.'),((5,4),'.'),((5,5),'.'),((5,6),'.'),((5,7),'.'),((5,8),'.'),((6,1),'.'),((6,2),'.'),((6,3),'.'),((6,4),'.'),((6,5),'.'),((6,6),'.'),((6,7),'.'),((6,8),'.'),((7,1),'P'),((7,2),'.'),((7,3),'P'),((7,4),'.'),((7,5),'P'),((7,6),'.'),((7,7),'P'),((7,8),'.'),((8,1),'.'),((8,2),'P'),((8,3),'.'),((8,4),'P'),((8,5),'.'),((8,6),'P'),((8,7),'.'),((8,8),'P')] This is the code I wrote so far: module Checkers where import Test.HUnit import Test.QuickCheck import Data.Char import Data.Maybe (fromJust) import Control.Error.Util (note) import Data.Maybe (listToMaybe) import Data.Char(isDigit) import Data.String import Data.List import Prelude type Board = [[Square]] type Square = Maybe Piece data Piece = Piece PColor PType deriving (Show) data PColor = White | Black deriving (Show) data PType = Pawn | Queen deriving (Show) typeList:: [(Char, PType)] typeList = [('p', Pawn), ('q', Queen)] initialBoard = unlines ["p.p.p.p." ,".p.p.p.p" ,"p.p.p.p." ,"........" ,"........" ,"........" ,"P.P.P.P." ,".P.P.P.P" ,"P.P.P.P." ] board2 = unlines ["p.p.p.p." ,".p.p.p.p" ,"p.p.p.p." ,".P.P.P.." ,"........" ,"........" ,"P...P.P." ,".P.P.P.P" ,"P.P.P.P."] showBoard :: Board -> String showBoard = unlines. map showRow where showRow = map showSquare readBoard :: String -> Either String Board readBoard = (mapM . mapM) readSquare . lines showSquare:: Square -> Char -- showSquare Nothing = ' ' -- showSquare (Just p) = showPiece p showSquare = maybe ' ' showPiece readSquare:: Char -> Either String Square readSquare '.' = return Nothing readSquare c = note errorMsg $ fmap return (readPiece c) where errorMsg = "Error reading square '" ++ show c ++ "' is not a valid square" --readSquare:: Char -> Square --readSquare c = readPiece c showPiece:: Piece -> Char showPiece (Piece White Pawn) = 'P' showPiece (Piece Black Pawn) = 'p' showPiece (Piece White Queen) = 'Q' showPiece (Piece Black Queen) = 'q' readPiece:: Char -> Maybe Piece readPiece c = fmap makePiece lookupType where color = if isUpper c then White else Black lookupType = lookup (toLower c) typeList makePiece = Piece color --readPiece 'P' = Just (Piece White Pawn) --readPiece 'p' = Just (Piece Black Pawn) --readPiece 'Q' = Just (Piece White Queen) --readPiece 'q' = Just (Piece Black Queen) --readPiece _ = Nothing --transform chessboard into a list of tuples to analyze possible kills --String or Int? testString = "hello world 13 i am a new 37 developer 82" data StringOrInt = S String | I Int deriving (Eq,Ord,Show) readInt :: String -> Int readInt = read --convert String into tuples --1. convert chessBoard into a list myShow :: String -> String myShow s = concat ["[", intersperse ',' s, "]"] isSlash x = x=='\\' deleteAllInstances :: Eq a => a -> [a] -> [a] deleteAllInstances a xs = filter (/= a) xs clearBoardList_ s = deleteAllInstances '\n' $ myShow $ s clearBoardList__ s = deleteAllInstances '[' $ clearBoardList_ s clearBoardList s = deleteAllInstances ',' $ clearBoardList__ s --2 zip with coordinates (1,1), (1,2).... (8,8) makeL = [(x,y)| x<-[1..8], y<-[1..8]] makeTuplesBoard s = zip makeL s testList = makeList initialBoard testList2 = makeList board2 --3 all together makeList s = makeTuplesBoard $ clearBoardList s --xy coordinates of pawns --is there my Pawn? isMyPawn ((x,y),z) = (z=='p' || z=='q') matchFirst (a,b) ((c,d),_) = (a,b) == (c,d) whatIsThere (a,b) list = eliminate $ find (matchFirst (a,b)) list --test: whatIsThere (1,1) $ makeList initialBoard eliminate (Just a) = a whichPiece (a,b) list = snd $ snd ( whatIsThere (a,b) $ makeTuplesBoard list ) --shows what is on a specific field isThereSth (a,b) list = whichPiece (a,b) list == 'p' || whichPiece (a,b) list == 'P' || whichPiece (a,b) list == 'q' ||whichPiece (a,b) list == 'Q' --isThereSth (1,1) $ makeList initialBoard isThereMyPawn (a,b) list = ((whichPiece (a,b) list == 'p'), list) --whichPiece (a,b) list == ((a,b),'p') isThereMyQueen (a,b) list = ((whichPiece (a,b) list == 'q'), list) isThereOtherPawn (a,b) list = ((whichPiece (a,b) list == 'P'), list) isThereOtherQueen (a,b) list = ((whichPiece (a,b) list == 'Q'), list) --remove a figure from its place and put somewhere else removePiece (a,b) list = map (\ x -> if matchFirst (a,b) x then ((a,b),'.') else x) list removeMyPawn (a,b) list = removePiece (a,b) list removeMyQueen (a,b) list = removePiece (a,b) list removeOtherPawn (a,b) list = removePiece (a,b) list removeOtherQueen (a,b) list = removePiece (a,b) list isWithinLimit (a,b) | not ((a>0) && (a<9) && (b>0) && (b<9)) = False | otherwise = True isWithinLimit1 (a,b) list | not ((a>0) && (a<9) && (b>0) && (b<9)) = (False, list) | otherwise = (True, list) putPiece (a,b) piece list = map (\ x -> if matchFirst (a,b) x then ((a,b),piece) else x) list --map (\ x -> if matchFirst (a,b) x then ((a,b),'.') else x) list --test: movePiece (1,1) (1,2) $ makeTuplesBoard initialBoard movePiece (a,b) (c,d) list = removePiece (a,b) $ putPiece (c,d) (whichPiece (a,b) $ makeTuplesBoard initialBoard ) (makeTuplesBoard initialBoard) --putADot (a,b) list = replace ( matchFirst (a,b)) list --swapTuples (a,b) (c,d) list = --move (a,b) (c,d) list = -- | (isThereSth (a,b) == False) = list -- | otherwise = isThereOtherPawn2 (a,b) list x | (x==True) = fst $ isThereOtherPawn (a,b) list | otherwise = False isWithinLimit2 (a,b) list x | (x==True) = fst $ isWithinLimit1 (a,b) list | otherwise = False isFree2 (a,b) list x | (x==True) = isFree (a,b) list | otherwise = False isThereMyPawn2 (a,b) list x | (x==True) = fst $ isThereMyPawn (a,b) list | otherwise = False isFree (a,b) list = not (isThereSth (a,b) list) isJumpLFPossible (a,b) list = isThereMyPawn2 (a,b) list $ (isFree2 (a+2,b-2) list $ isWithinLimit2 (a+2,b-2) testList $ isThereOtherPawn2 (a+1,b-1) list $ fst $ isWithinLimit1 (a+1,b-1) list) --test: isFree2 (3,4) testList $ isWithinLimit2 (3,4) testList $ isThereOtherPawn2 (3,4) testList $ fst $ isWithinLimit1 (2,3) testList isJumpRFPossible (a,b) list = isThereMyPawn2 (a,b) list $ (isFree2 (a+2,b+2) list $ isWithinLimit2 (a+2,b+2) testList $ isThereOtherPawn2 (a+1,b+1) list $ fst $ isWithinLimit1 (a+1,b+1) list) --test: isFree2 (3,4) testList $ isWithinLimit2 (3,4) testList $ isThereOtherPawn2 (3,4) testList $ fst $ isWithinLimit1 (2,3) testList -- checking whether my Pawn has any jump possiblitiy - one move canJumpLF (a,b) list | (isJumpLFPossible (a,b) list) = [(a,b),(a+2, b-2)] | otherwise = [] --test: canJump (1,1) testBoard canJumpRF (a,b) list | (isJumpRFPossible (a,b) list) = [(a,b),(a+2, b+2)] | otherwise = [] --test: canJump (1,1) testBoard isFree (a,b) list = not (isThereSth (a,b) list) -- recursive check whether and which kills are possible for my Pawn --canJump (a,b) list -- | (fst (canJumpLF (a,b) list)) = snd (canJump (a+2, b-2) list) -- | (fst (canJumpRF (a,b) list)) = snd (canJump (a+2, b+2) list) -- | otherwise = [] replaceTuple tups old new = map check tups where check tup | tup == old = new | otherwise = tup --movePawn (x,y) (a,b) = if (isMyPawn(x,y) --replacePawn list = replaceTuple $ ((x,y),_) ((x,y),'.') list --analyze possible moves of pawn --Tests tests:: Test tests = TestList $ map TestCase [assertEqual "odd tests here" 1(1 :: Int)] prop_empty :: Int -> Bool prop_empty c1 = (c1::Int) == c1 runTest = do return() main:: IO() main = runTest My problem is as follows. I need a function that returns a list of all possible jump sequences. I think it needs to be a recursive function. It should: (1) check whether a jump to the right, left is possible (2) if it is possible, then recursively run itself from the position a pawn would take after (1) (3) it should return a list of tuples' lists representing possible sequences of jumps: [(a,b), (c,d), (e,f), (g,h)], [(a,b), (p,r)], [(a,b), (q,s), (t,u)]] (4) if the pawn reached the opposite end of the chessboard it can jump backwards if there are any possible jumps (5) if the pawn reached the end of the board and there are no jumps possible it turns into a queen (it gets crowned - I cannot tell whether this possibility should be included in this function or not - perhaps not) In other words, from the position (a,b) I want to analyze all possible jumps and write a function that returns a list of all possible jump sequences. ... After modifications my problem remains but I can explain it simpler: The function canOneJump (a,b) board returns a list of possible places where pawns can be after they made 1 jump. In other words, the function returns [(1,2), (2,3), (4,5)] each tuple representing a row and a column where a pawn can be after a jump. I have a function that is supposed to create now lists of jumps from the initial location of a pawn (a,b) (based on the chessboard situation which is given as a list) but it does not work. Perhaps someone could help me to fix this function so it works. I want to get a list of jump sequences [[(3,3), (5,5), (7,3)], [(3,3), (5,1)]] that represent different jump sequences that are available. canJump v board = map (v:) w where list = listPlacesAfterMyPawnJump v board w = concat $ map (flip canJump board) list
First I'd suggest posting your code to the Code Review Stackexchange to get some pointers on code style, organization, and other tips. They have a rule about only reviewing working code, so just ask them to review the code that you have. Here's an outline of how I would proceed. The solution will be a lot easier to understand with these type definitions: type Coord = (Int,Int) type CoordBoard = [ (Coord, Char) ] Step 1. Using the functions you already have, write a function to return all possible single jumps from a specific square: singleJumps :: (Coord, CoordBoard) -> [ (Coord, CoordBoard) ] Note that you return the updated CoordBoard - i.e. the board with the jumped piece removed and the jumper moved. Return the empty list if there are no possible jumps. Step 2. Then write a function to find all possible jump paths from a starting square: multiJumps :: (Coord, CoordBoard) -> [ ([Coord], CoordBoard) ] This also returns the CoordBoard with the jump moves executed. The idea behind multiJumps is: for each possible single jump (rc, b): for each possible multi jump (path, b') starting from (rc,b): return the path (rc:path) and ending board configuration b' This is where the recursion happens. (Hint: multijumps can be written as a list comprehension.)
Finally, I solved my problem, but I had to change several functions / write new ones. canJumpLB (a,b) list | (isJumpLBPossible (a,b) list) = [(a,b),(a-2, b-2)] | otherwise = [] canJumpRB (a,b) list | (isJumpRBPossible (a,b) list) = [(a,b),(a-2, b+2)] | otherwise = [] canOneJump (a,b) list =filter (/=[]) $filter (/=[]) $filter (/=[]) [canJumpLF (a,b) list] ++ [canJumpRF (a,b) list] canImakeAnotherJump list listOfLists = concat $ [canOneJump (x!!((length x)-1)) list | x <- listOfLists] anotherJump list listOfLists = combine (canImakeAnotherJump list listOfLists) listOfLists [] jumpSequences v list [] | (canOneJump v list == []) = [] | otherwise = jumpSequences v list (canOneJump v list) jumpSequences v list results | ((canImakeAnotherJump list results) == []) = results | otherwise = jumpSequences v list (anotherJump list results) The function jumpSequences shows all sequences of jumps from a certain position. My pawns do not jump backwards, so I do not update the chessboard.