🎉 Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io 🎉
Avatar of BrightOne

BrightOne's solution

to Queen Attack in the Haskell Track

Published at Feb 02 2021 · 0 comments
Instructions
Test suite
Solution

Given the position of two queens on a chess board, indicate whether or not they are positioned so that they can attack each other.

In the game of chess, a queen can attack pieces which are on the same row, column, or diagonal.

A chessboard can be represented by an 8 by 8 array.

So if you're told the white queen is at (2, 3) and the black queen at (5, 6), then you'd know you've got a set-up like so:

_ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _
_ _ _ W _ _ _ _
_ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _
_ _ _ _ _ _ B _
_ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _

You'd also be able to answer whether the queens can attack each other. In this case, that answer would be yes, they can, because both pieces share a diagonal.

Hints

To complete this exercise, you need to implement the following functions:

  • boardString
  • canAttack

You will find the type signatures already in place, but it is up to you to define the functions.

Positions are specified as two-tuples:

  • The first element is the row (rank in Chess terms).
  • The second element is is the column (file in Chess terms).
  • (0, 0) is the top left of the board, (0, 7) is the upper right,
  • (7, 0) is the bottom left, and (7, 7) is the bottom right.

Getting Started

Please refer to the installation and learning help pages.

Running the tests

To run the test suite, execute the following command:

stack test

If you get an error message like this...

No .cabal file found in directory

You are probably running an old stack version and need to upgrade it.

Otherwise, if you get an error message like this...

No compiler found, expected minor version match with...
Try running "stack setup" to install the correct GHC...

Just do as it says and it will download and install the correct compiler version:

stack setup

Running GHCi

If you want to play with your solution in GHCi, just run the command:

stack ghci

Feedback, Issues, Pull Requests

The exercism/haskell repository on GitHub is the home for all of the Haskell exercises.

If you have feedback about an exercise, or want to help implementing a new one, head over there and create an issue. We'll do our best to help you!

Source

J Dalbey's Programming Practice problems http://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

Tests.hs

{-# OPTIONS_GHC -fno-warn-type-defaults #-}

import Data.Foldable     (for_)
import Test.Hspec        (Spec, describe, it, shouldBe)
import Test.Hspec.Runner (configFastFail, defaultConfig, hspecWith)

import Queens (boardString, canAttack)

main :: IO ()
main = hspecWith defaultConfig {configFastFail = True} specs

specs :: Spec
specs = do

    -- Track-specific test cases.

    describe "boardString" $ do

      it "empty board" $ boardString Nothing Nothing
        `shouldBe` unlines [ "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _" ]

      it "board with just white queen" $ boardString (Just (2, 4)) Nothing
        `shouldBe` unlines [ "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ W _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _" ]

      it "board with just black queen" $ boardString Nothing (Just (0, 0))
        `shouldBe` unlines [ "B _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _" ]

      it "board" $ boardString (Just (2, 4)) (Just (6, 6))
        `shouldBe` unlines [ "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ W _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ _ _"
                           , "_ _ _ _ _ _ B _"
                           , "_ _ _ _ _ _ _ _" ]

    -- The function described by the reference file as `create` doesn't
    -- exist in this track, so only the `canAttack` test cases were
    -- implemented here

    describe "canAttack" $ do

      let test (description, white, black, expected) =
            it description $ canAttack white black `shouldBe` expected

          cases = [ ("can not attack"               , (2, 4), (6, 6), False)
                  , ("can attack on same rank"      , (2, 4), (2, 6), True )
                  , ("can attack on same file"      , (4, 5), (2, 5), True )
                  , ("can attack on first diagonal" , (2, 2), (0, 4), True )
                  , ("can attack on second diagonal", (2, 2), (3, 1), True )
                  , ("can attack on third diagonal" , (2, 2), (1, 1), True )
                  , ("can attack on fourth diagonal", (2, 2), (5, 5), True ) ]

      for_ cases test

-- cc646595d39e13c4d310da2629599bcc45e92bd9
module Queens (boardString, canAttack) where

import Control.Lens (element, set)

boardString :: Maybe (Int, Int) -> Maybe (Int, Int) -> String
boardString white black = unlines $ map unwords $ place "W" white $ place "B" black emptyBoard
  where
    emptyBoard = replicate 8 $ replicate 8 "_"
    place c (Just (r, f)) = set (element r . element f) c
    place _ Nothing = id

canAttack :: (Int, Int) -> (Int, Int) -> Bool
canAttack (r1, f1) (r2, f2) = sameRank || sameFile || sameDiagonal
  where
    sameRank = r1 == r2
    sameFile = f1 == f2
    sameDiagonal = abs (r1 - r2) == abs (f1 - f2)

Community comments

Find this solution interesting? Ask the author a question to learn more.

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?