DEV Community


Implementing the rules of 3 Man Chess: Variant “In The Round”. Part 1, Fundamental Types

Michał Krzysztof Feiler
An Informatics undergrad first-year who used to like Go, then Clojure, after LiquidHaskell heading Rust,C,Nim,Coq,Lean. @UAMPoznań
Originally published at Medium on ・5 min read

This post was originally published in April 2018 on Medium. The project described in it is temporarily discontinued. This post is not even up-to-date with the project. I'm just trying out the Medium→Dev.To migration feature.

For the past three years, my go-to project for learning new programming languages and paradigms was the implementation of the rules of 3 Man Chess: Variant “In The Round”. I have used it to learn much about Go, Dart, Java, Clojure and Haskell. All the implementation attempts are available on my Github profile. So what’s the matter with the “attempts” word, why none of them is working, what’s so difficult? This is what this series is going to be about. But these it this “pilot” article, in this part, will be just fundamental data types because I don’t have much time. I have festival presentation deadlines for this project. One of them is today, in four hours. So, if you could look at the entirety of the code and help me, please do.

First, the board. It is a circular board, consisting of six rings, later called Ranks, numbered from 0 (MostOuter) to 5 (MostInner).

data Rank = MostOuter | SecondOuter | MiddleOuter 
 | MiddleInner | SecondInner | MostInner 
 deriving (Eq, Ord, Read, Show)
ranks :: [Rank]
inw :: Rank -> Rank
out :: Rank -> Maybe Rank

Each of those ranks consists of 24 Files.

The board is divided in three, one for each of the players. Opponents’ rooks are next to each other, but they cannot capture each other because of a feature called “moats”. Neither can pawns, because of a feature called “creeks”. If you want to know how it looks or have other questions, may answer them.

  1. Set all Kings on white. White goes first, then clockwise to gray, then black.
data Color = White | Gray | Black
 deriving (Eq, Ord, Read)
next :: Color -> Color
prev :: Color -> Color

So now we also need some data type that would correspond to number from 0 to 7, why not just use an Integer type? Because we want to be protected with our Haskellish type system, and another way we do this data type of ours may turn beneficial to us in some ways. So. Say hello to

data SegmentHalf = FirstHalf | SecondHalf 
 deriving (Eq, Ord, Read, Show)
data SegmentQuarter = SegmentQuarter 
 { half :: SegmentHalf
 , halfQuarter :: SegmentHalf }
 deriving (Eq, Ord, Read, Show)
data SegmentEight = SegmentEight 
 { segmentQuarter :: SegmentQuarter
 , quarterHalf :: SegmentHalf } 
 deriving (Eq, Ord, Read, Show)
type ColorSegment = Color
data File = File { segmColor :: ColorSegment
 , colorSegmFile :: SegmentEight }
 deriving (Eq, Ord, Read, Show)
opposite :: File -> File
plus :: File -> File
minus :: File -> File

-- and finally
type Pos = (Rank, File)
rank :: Pos -> Rank
file :: Pos -> File

-- |'kfm' is where the King stands
kfm :: SegmentEight
kfm = (SegmentEight (SegmentQuarter SecondHalf FirstHalf) FirstHalf)

Okay, enough about the board coordinates data structures.

  1. The center of the board may be passed through, but has no square to be occupied.

a. Adjoining vertical squares, through the center, maintain the same color.

i. Queen, King, Rook, or Pawn, advancing 1 square through center, retains same color.

ii. Knight also will retain same color.

iii. This phenomenon is necessary to maintain ‘round board’ integrity. As players should be aware of this, conventional Chess strategy is not compromised.


  1. VERTICAL MOVES (Queen, King, Rook, Pawn)

a. Follows straight, (and if necessary, through the center), along the checkerboard file.

And a Pawn can go only forward. So, we do not want to prove that it is not possible for a Pawn to perform so many captures as to it become unsure if it’s directed inwards or outwards, and am unsure if I had proven it to be impossible sometime ago already, but anyway such distinction helps. Therefore,

data FigType = InwardPawn | OutwardPawn | Rook | Knight
 | Bishop | King | Queen
 deriving (Eq, Read)

--for promotion purposes, we will also define
data Promotion = RookPromotion | KnightPromotion |
 | BishopPromotion | QueenPromotion
 deriving (Eq, Show)
desiredType :: Promotion -> FigType
  1. HORIZONTAL MOVES (Queen, King, Rook)

a. Rotate about the center on the same row (rank).

So the moves are, like, modulo 24.

  1. DIAGONAL MOVES (Queen, King, Bishop, or Pawn capturing)

a. Follow 1 of the 2 trajectory lines out from the square it rest on.

On the board, there are 48 diagonal trajectory lines, pairs of them crossing over each one of the squares. On the MostOuter rank, on each of the files a pair of trajectory lines forms a corner. Each of those pairs forms a loop over the center.

b. May rotate through the center.

c. Can not ‘turn the corner’ at the outer rank, in 1 move.

Also, what is not included in the rules but what was stated to be the right thing by Mr Clif King, the owner of 3 Man Chess, and I guess their creator, in our correspondence when I asked for this specifically and argumented for it, and which is not yet included in the rules, the figure must not go diagonally through the center or filewise(horizontally) around the center back to the square it went from — it would cause oft something like a lack of zugzwang (compulsion to move), an important feature of chess, and would have serious consequences in gameplay, especially in case of computer gameplay.


a. 2 squares vertically then 1 square horizontally, or

b. 1 square vertically then 2 horizontally.

c. Use 1 of the 2 methods above (i.e., do not follow a trajectory line through the center). Also, these 2 methods are necessary for BORDER CONTROL described below.

These are just about the same, until when it comes to “border control”, i.e. “moats”.

So, there are multiple ways to perform a single from→to move, which we need to handle separately to avoid unnecessary computations. Therefore, we need data structures for that

data Orientation = Rankwise | Filewise deriving (Eq, Read, Show)
data RankwiseDirection = Inwards | Outwards
 deriving (Eq, Show)
data FilewiseDirection = Pluswards | Minuswards
 deriving (Eq, Show)
filewiseInc :: FilewiseDirection -> File -> File

data DiagonalDirection = DiagonalDirection
 deriving (Eq, Show)
data Count = Once | OnceMore Count deriving (Eq, Read, Show)

class (Eq a) => LinearDirection a where
 addOne :: a -> Pos -> Maybe Pos
instance LinearDirection RankwiseDirection where
 addOne Inwards (MostInner, file) = 
 Just (MostInner, opposite file)
 addOne Inwards (rank, file) = 
 Just (inw rank, file)
 addOne Outwards (rank, file) =
 do { o <- out rank;
 return (o, file) }
instance LinearDirection FilewiseDirection where
 addOne w (rank, file) = Just (rank, filewiseInc w file)
instance LinearDirection DiagonalDirection where
 addOne (DiagonalDirection Inwards p)
 (MostInner, file) = ... --long case expressions here
 addOne (DiagonalDirection Inwards p) (rank, file) = 
 Just (inw rank, filewiseInc p file)
 addOne (DiagonalDirection Outwards p) (rank, file) = 
 do { o <- out rank; return (o, filewiseInc p file) }

I have to go back to work now, and I am not sure if I have good reasons not to publish this beginning. There were almost no difficulties so far in this post, though even those few caveats took me many man-days of thinking back in the days. I am writing this post to motivate myself and to clarify the fundamentals of my code. Aaaand I’m hoping for collaborators. Really. Any. Any collaborators. You don’t need to know Haskell for more than a few hours, you will learn Haskell along the way.


If you want more information about something like “why don’t the rooks capture each other” or “what does it all look like”, look at

Should I update this post later or just post next parts? Maybe posting next parts will be better.

Discussion (0)