How to resolve the algorithm 15 puzzle game step by step in the Haskell programming language

Published on 7 June 2024 03:52 AM

How to resolve the algorithm 15 puzzle game step by step in the Haskell programming language

Table of Contents

Problem Statement

Implement the Fifteen Puzzle Game.

The   15-puzzle   is also known as:

Let's start with the solution:

Step by Step solution about How to resolve the algorithm 15 puzzle game step by step in the Haskell programming language

Explanation of the Haskell Code:

Importing Libraries:

import Data.Array
import System.Random
  • Data.Array provides functions for working with arrays.
  • System.Random provides functions for generating random numbers.

Main Function:

main :: IO ()
  • The main function is the entry point of the program.

User Input and Difficulty Selection:

putStrLn "Please enter the difficulty level: 0, 1 or 2"
userInput <- getLine
let diffLevel = read userInput
  • Gets user input for the difficulty level, which can be 0, 1, or 2.

Validating User Input:

if userInput == "" || any (\c -> c < '0' || c > '9') userInput || diffLevel > 2 || diffLevel < 0
   then putStrLn "That is not a valid difficulty level." >> main
  • Checks if the user input is empty, contains non-digits, or is outside the valid difficulty range. If so, it prompts the user for correct input.

Game Loop Function:

gameLoop :: Puzzle -> IO ()
  • gameLoop is a recursive function that runs the game. It takes the current puzzle state as input.

Checking if the Puzzle is Solved:

if puzzle == solvedPuzzle = putStrLn "You solved the puzzle!" >> printPuzzle puzzle
  • Compares the current puzzle to the solvedPuzzle and prints a success message if they are equal.

Otherwise, Displaying the Puzzle and Getting User Input:

else do
   printPuzzle puzzle
   putStrLn "Please enter number to move"
   userInput <- getLine
  • Prints the puzzle and prompts the user to enter a number to move.

Validating User Move:

if any (\c -> c < '0' || c > '9') userInput
   then putStrLn "That is not a valid number." >> gameLoop puzzle
  • Checks if the user input is empty or contains non-digits. If so, it prompts the user for correct input.

Finding Valid Moves:

let move = read userInput
   validMoves puzzle = [puzzle ! (row', column') |
                    row' <- [rowEmpty-1..rowEmpty+1], column' <- [columnEmpty-1..columnEmpty+1],
                    row' < 4, row' >= 0, column' < 4, column' >= 0,
                    (row' == rowEmpty) /= (column' == columnEmpty)]
  • validMoves finds all the valid moves around the empty tile in the puzzle.

Applying the Move:

gameLoop (applyMove move puzzle)
  • If the user's move is valid, it applies the move using applyMove and continues the game loop.

Finding the Empty Tile's Index:

findIndexOfNumber :: Int -> Puzzle -> (Int, Int)
findIndexOfNumber number puzzle = case filter (\idx -> number == puzzle ! idx)
                                             (indices puzzle) of
                                     [idx] -> idx
                                     _ -> error "BUG: number not in puzzle"
  • findIndexOfNumber finds the index of the empty tile in the puzzle.

Applying a Move:

applyMove :: Int -> Puzzle -> Puzzle
applyMove numberToMove puzzle = puzzle // [(indexToMove, 16), (emptyIndex, numberToMove)]
  • applyMove applies a move by swapping the empty tile and the tile with the specified number.

Printing the Puzzle:

printPuzzle :: Puzzle -> IO ()
  • printPuzzle prints the current puzzle state using a custom formatting function.

Generating a Solved Puzzle:

solvedPuzzle :: Puzzle
solvedPuzzle = listArray ((0, 0), (3, 3)) [1..16]
  • solvedPuzzle represents the solved puzzle where numbers are arranged in order from 1 to 16.

Shuffling the Puzzle:

shufflePuzzle :: Int -> Puzzle -> IO Puzzle
shufflePuzzle 0 puzzle = return puzzle
shufflePuzzle numOfShuffels puzzle = do
   let moves = validMoves puzzle
   randomIndex <- randomRIO (0, length moves - 1)
   let move = moves !! randomIndex
   shufflePuzzle (numOfShuffels - 1) (applyMove move puzzle)
  • shufflePuzzle shuffles the puzzle by applying random valid moves. It takes the number of shuffles and the current puzzle as input.

Source code in the haskell programming language

import Data.Array
import System.Random

type Puzzle = Array (Int, Int) Int

main :: IO ()
main = do
    putStrLn "Please enter the difficulty level: 0, 1 or 2"
    userInput <- getLine
    let diffLevel = read userInput
    if userInput == "" || any (\c -> c < '0' || c > '9') userInput || diffLevel > 2 || diffLevel < 0
        then putStrLn "That is not a valid difficulty level." >> main
        else shufflePuzzle ([10, 50, 100] !! diffLevel) solvedPuzzle >>= gameLoop

gameLoop :: Puzzle -> IO ()
gameLoop puzzle
    | puzzle == solvedPuzzle = putStrLn "You solved the puzzle!" >> printPuzzle puzzle
    | otherwise = do
    printPuzzle puzzle
    putStrLn "Please enter number to move"
    userInput <- getLine
    if any (\c -> c < '0' || c > '9') userInput
        then putStrLn "That is not a valid number." >> gameLoop puzzle
        else let move = read userInput in
            if move `notElem` validMoves puzzle
                then putStrLn "This move is not available." >> gameLoop puzzle
                else gameLoop (applyMove move puzzle)

validMoves :: Puzzle -> [Int]
validMoves puzzle = [puzzle ! (row', column') |
                     row' <- [rowEmpty-1..rowEmpty+1], column' <- [columnEmpty-1..columnEmpty+1],
                     row' < 4, row' >= 0, column' < 4, column' >= 0,
                     (row' == rowEmpty) /= (column' == columnEmpty)]
    where (rowEmpty, columnEmpty) = findIndexOfNumber 16 puzzle

applyMove :: Int -> Puzzle -> Puzzle
applyMove numberToMove puzzle = puzzle // [(indexToMove, 16), (emptyIndex, numberToMove)]
    where indexToMove = findIndexOfNumber numberToMove puzzle
          emptyIndex = findIndexOfNumber 16 puzzle

findIndexOfNumber :: Int -> Puzzle -> (Int, Int)
findIndexOfNumber number puzzle = case filter (\idx -> number == puzzle ! idx)
                                              (indices puzzle) of
                                      [idx] -> idx
                                      _ -> error "BUG: number not in puzzle"

printPuzzle :: Puzzle -> IO ()
printPuzzle puzzle = do
    putStrLn "+--+--+--+--+"
    putStrLn $ "|" ++ formatCell (0, 0) ++ "|" ++ formatCell (0, 1) ++ "|" ++ formatCell (0, 2) ++ "|" ++ formatCell (0, 3) ++ "|"
    putStrLn "+--+--+--+--+"
    putStrLn $ "|" ++ formatCell (1, 0) ++ "|" ++ formatCell (1, 1) ++ "|" ++ formatCell (1, 2) ++ "|" ++ formatCell (1, 3) ++ "|"
    putStrLn "+--+--+--+--+"
    putStrLn $ "|" ++ formatCell (2, 0) ++ "|" ++ formatCell (2, 1) ++ "|" ++ formatCell (2, 2) ++ "|" ++ formatCell (2, 3) ++ "|"
    putStrLn "+--+--+--+--+"
    putStrLn $ "|" ++ formatCell (3, 0) ++ "|" ++ formatCell (3, 1) ++ "|" ++ formatCell (3, 2) ++ "|" ++ formatCell (3, 3) ++ "|"
    putStrLn "+--+--+--+--+"
    where formatCell idx
              | i == 16 = "  "
              | i > 9 = show i
              | otherwise = " " ++ show i
              where i = puzzle ! idx

solvedPuzzle :: Puzzle
solvedPuzzle = listArray ((0, 0), (3, 3)) [1..16]

shufflePuzzle :: Int -> Puzzle -> IO Puzzle
shufflePuzzle 0 puzzle = return puzzle
shufflePuzzle numOfShuffels puzzle = do
    let moves = validMoves puzzle
    randomIndex <- randomRIO (0, length moves - 1)
    let move = moves !! randomIndex
    shufflePuzzle (numOfShuffels - 1) (applyMove move puzzle)


  

You may also check:How to resolve the algorithm First-class functions step by step in the J programming language
You may also check:How to resolve the algorithm 100 doors step by step in the Klingphix programming language
You may also check:How to resolve the algorithm Memory allocation step by step in the Ada programming language
You may also check:How to resolve the algorithm Cholesky decomposition step by step in the Delphi programming language
You may also check:How to resolve the algorithm Quaternion type step by step in the zkl programming language