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.

Resources