# 4995 Final Project
### by Joe Huang (jch2220)

This is the final project for COMS 4995 attempting to solve the 
freecell games. The project includes an algorithm to deal cards 
for the game, the Freecell game, and the solver. 

In this readme, we will first introduce the game of freecell, 
one example of running the game controller and of running the solver,
the details the purpose and key functions in each files, and finally
a full demo of the game and the solver. 

## What is Freecell? 

Freecell is a card game consists of piles of cards, four freecells, 
and four foundations. The goal of the game is to place all the cards 
in the foundations. Each foundations represent one suit of the card 
and to place a card in a foundation, that card must be in the same 
suit and one rank higher (except for Ace which can be placed when the 
foundation is empty). Players can move the top-most card from one pile 
to another or from freecell to a pile as long as placing card is one 
rank lower and in opposite color. Players can leverage the freecells 
to put any card they want from the piles. Once a card is placed in the
foundation, it cannot be removed. In addition, multiple cards can be
moved together from pile to pile if they are in desending order from 
the top and in alternating color. The number of cards they can move 
at once is one more than all the current free spaces on the board 
(freecell or empty pile column).  

## Example 

### Running the Game Controller

To run the game, the following APIs are used:

start <gameNumber>: this command takes a game number and output a
                    game status. The user will need to keep the game 
                    status to make the next move.

makeMove <gameStatus> <move>: this command takes a game status and a 
                              move. It returns two values: a boolean 
                              value represent whether the move was valid 
                              and a new game status if the move was
                              applied. 

An example to run would be the following:
```
> stack ghci 
Prelude>:l gameController
*GameController>g1 = start 1
*GameController>(valid, g2) = makeMove g1 (MoveCasToFC (Card Six Spade) 0)
*GameController>g2

FC: 6S
FD: [0,0,0,0]
1  2  3  4  5  6  7  8 
JD 2D 9H JC 5D 7H 7C 5H
KD KC 9S 5S AD QC KH 3H
2S KS 9D QD JS AS AH 3C
4C 5C TS QH 4H AC 4D 7S
3S TD 4S TH 8H 2C JH 7D
6D 8S 8D QS 6C 3D 8C TC
   9C 2H 6H            

All Possible Moves: [MoveCasToFC (Card Six Diamond) 0,MoveCasToFC (Card Nine Club) 1,MoveCasToFC (Card Two Heart) 2,MoveCasToFC (Card Six Heart) 3,MoveCasToFC (Card Six Club) 4,MoveCasToFC (Card Three Diamond) 5,MoveCasToFC (Card Eight Club) 6,MoveCasToFC (Card Ten Club) 7]
```

The user can print the game status to get all the possible move they can take.

### Running the solver

The solver can be run directly as executable. Once it is ran, a 
message will ask the user to input a game number. If the number 
is valid, the solver will return all the steps from start to 
finish once a solution is found.

To run the solver, enter the following command:
```
>stack ghc -- -O2 -Wall solver.hs -threaded -rtsopts -eventlog
>./solver +RTS -N2 -s
Please enter a game number:
164
[MoveCasToCas (Card Six Spade) 0 6,MoveCasToFC (Card Queen Heart) 3
...(All the actions lead to winning)
```

## File Description

### deal.hs

This file handles the card dealing in the free cell. The deal allows 
input number from 1 to 32000 and follows the algorithm developed by 
Jim Horne. 

The algorithm are as followed:

1. Seed the RNG with the number of the deal.
2. Create an array of 52 cards: Ace of Clubs, Ace of Diamonds, Ace of 
    Hearts, Ace of Spades, 2 of Clubs, 2 of Diamonds, and so on through 
    the ranks: Ace, 2, 3, 4, 5, 6, 7, 8, 9, 10, Jack, Queen, King. The
    array indexes are 0 to 51, with Ace of Clubs at 0, and King of 
    Spades at 51.
3. Until the array is empty:
    - Choose a random card at index ≡ next random number (mod array length).
    - Swap this random card with the last card of the array.
    - Remove this random card from the array. (Array length goes down by 1.)
    - Deal this random card.
4. Deal all 52 cards, face up, across 8 columns. The first 8 cards go
    in 8 columns, the next 8 cards go on the first 8 cards, and so on.

To deal a certain game number, run the following:
```
Prelude> :l deal
*Deal> startAndShowDeal 1
["JD","2D","9H","JC","5D","7H","7C","5H"]
["KD","KC","9S","5S","AD","QC","KH","3H"]
["2S","KS","9D","QD","JS","AS","AH","3C"]
["4C","5C","TS","QH","4H","AC","4D","7S"]
["3S","TD","4S","TH","8H","2C","JH","7D"]
["6D","8S","8D","QS","6C","3D","8C","TC"]
["6S","9C","2H","6H"]
```

### game.hs

This file describes the freecell game structure. Here it defines the 
data structure for the game component, show (print) functions, loading
functions to load the deal from deal.hs, game functions that provide 
all possible move given a game status, apply functions that apply move
to a game status, and some helper functions.

The Game consists of several components: 
Rank, Suit, Card, Cascade, Cascades, Freecell, Foundation, Stack,Game,
and Move. Each card is made of a Rank and a Suit. Each Cascade and 
Freecell is made of a list of Card. A Foundation is made of list of 
integer representing the current highest foundation for each suit. 
A Cascades is made of a list of Cascades. A Stack is made of a list
of cards that can be moved together. A Move specifies information 
about that move, including the type of move, the destination of the move
the card to be moved, etc. Finally, a Game consists of the cascades,
the foundation, and the freecell. 

An example of a game:
```
FC: 
FD: [0,0,0,0]
1  2  3  4  5  6  7  8 
JD 2D 9H JC 5D 7H 7C 5H
KD KC 9S 5S AD QC KH 3H
2S KS 9D QD JS AS AH 3C
4C 5C TS QH 4H AC 4D 7S
3S TD 4S TH 8H 2C JH 7D
6D 8S 8D QS 6C 3D 8C TC
6S 9C 2H 6H    
```

Given a game, the getAllMove function provides all the move that can 
be made. A list of moves are as followed:
- MoveCasToFC card from: Move a card from a cascade to the freecell. 
- MoveCasToFoun card from: Move a card from a cascade to the foundation. 
- MoveCasToCas card from to: Move a card from a cascade to another.
- MoveFCToCas card to: Move a card from freecell to a cascade. 
- MoveFCToFoun card: Move a card from freecell to foundation
- MoveMult card stack from to: Move a stack of cards from a cascade to
                               another.

```
*Game> game = initializeGame 1
*Game> getAllMove game
[MoveCasToFC (Card Six Spade) 0,MoveCasToFC (Card Nine Club) 1,MoveCasToFC (Card Two Heart) 2,MoveCasToFC (Card Six Heart) 3,MoveCasToFC (Card Six Club) 4,MoveCasToFC (Card Three Diamond) 5,MoveCasToFC (Card Eight Club) 6,MoveCasToFC (Card Ten Club) 7]
```

Once we have a valid move, we can apply the move to the game. The apply
function takes the game and the move, then returns the game after the move 
has been applied. 

```
*Game> game = initializeGame 1
*Game> getAllMove game
[MoveCasToFC (Card Six Spade) 0,MoveCasToFC (Card Nine Club) 1,MoveCasToFC (Card Two Heart) 2,MoveCasToFC (Card Six Heart) 3,MoveCasToFC (Card Six Club) 4,MoveCasToFC (Card Three Diamond) 5,MoveCasToFC (Card Eight Club) 6,MoveCasToFC (Card Ten Club) 7]
*Game> applyMove game (MoveCasToFC (Card Six Spade) 0)
applyMove game (MoveCasToFC (Card Six Spade) 0)
FC: 6S
FD: [0,0,0,0]
1  2  3  4  5  6  7  8 
JD 2D 9H JC 5D 7H 7C 5H
KD KC 9S 5S AD QC KH 3H
2S KS 9D QD JS AS AH 3C
4C 5C TS QH 4H AC 4D 7S
3S TD 4S TH 8H 2C JH 7D
6D 8S 8D QS 6C 3D 8C TC
   9C 2H 6H  
```

### gameController.hs

The game controller connects the user and the game as well as keeps 
track of the game history. This file simplifies the API to play the 
game as well as make it easier for the solver to make moves. The 
two command are as followed:

start <gameNumber>: return a gamestatus with the given game number
                    deal

makeMove <gameStatus> <move>: return a move status and tbe game 
                              status given a move. If the move is 
                              successful, move status would be set
                              to Success and the game status would 
                              reflect the change. If the move is not
                              valid, then the move stauts would be 
                              set to Fail with the original game 
                              status. Finally, if the game is finished
                              the move status would return Win.

```
*GameController> game = start 2
*GameController> makeMove game (MoveCasToFC (Card Three Heart) 0)
(Success,FC: 3H
FD: [0,0,1,1]
1  2  3  4  5  6  7  8 
QD QC KC 3C 4C 2C KD 5C
4D JD JS 6H QS 6D 2D 9C
TD JC 8C 6C 8S 4S 5D QH
7S 9D KS 7C 6S 4H AC 8H
   9S TC 2S 3S TS 9H 2H
   AD 7H 3D 5H 8D KH 7D
   5S TH JH            



All Possible Moves: [MoveCasToCas (Card Seven Spade) 0 5,MoveCasToFC (Card Seven Spade) 0,MoveCasToFC (Card Five Spade) 1,MoveCasToFC (Card Ten Heart) 2,MoveCasToFC (Card Jack Heart) 3,MoveCasToFC (Card Five Heart) 4,MoveCasToFC (Card Eight Diamond) 5,MoveCasToFC (Card King Heart) 6,MoveCasToFC (Card Seven Diamond) 7]
 
)
```

### solver.hs

The solver implements the Heineman’s Staged Deepening Heusirtic (HSDH) to
find a solution for a freecell game. HSDH first find all the states k steps 
away from the current one and rank them based on the heusirtic. The 
heusirtic checks the foundation's current cards and finds all the card that 
are supposed to be placed next. For those next cards, calculate how many 
cards are on top of them. The heusirtic mulplies that score by two if 
all freecell are used or any foundation cell is 0. Then, the best state are 
used for the next iteration until a solution is found.

## Demo

### Run a entire game from start to finish

```
*GameController> g1 = start 15140
*GameController> (_, g2)= makeMove g1 (MoveCasToCas (Card Queen Spade) 7 5)
*GameController> (_, g3) = makeMove g2 (MoveCasToCas (Card Eight Diamond) 3 6)
*GameController> (_, g4) = makeMove g3 (MoveCasToCas (Card Seven Spade) 1 6)
*GameController> (_, g5) = makeMove g4 (MoveCasToCas (Card Eight Spade) 2 3)
*GameController> (_, g6) = makeMove g5 (MoveCasToCas (Card Seven Heart) 4 3)
*GameController> (_, g7) = makeMove g6 (MoveCasToCas (Card Six Diamond) 4 6)
*GameController> (_, g8) = makeMove g7 (MoveCasToCas (Card Jack Club) 2 1)
*GameController> (_, g9) = makeMove g8 (MoveCasToCas (Card Four Spade) 7 2)
*GameController> (_, g10) = makeMove g9 (MoveMult (Card Nine Spade) (Stack [Card Nine Spade,Card Eight Diamond,Card Seven Spade,Card Six Diamond]) 6 7)
*GameController> (_, g11) = makeMove g10 (MoveCasToCas (Card Ten Spade) 6 4)
*GameController> (_, g12)= makeMove g11 (MoveMult (Card Nine Heart) (Stack [Card Nine Heart,Card Eight Spade,Card Seven Heart]) 3 4)
*GameController> (_, g13) = makeMove g12 (MoveMult (Card King Diamond) (Stack [Card King Diamond,Card Queen Spade]) 5 6)
*GameController> (_, g14) = makeMove g13 (MoveMult (Card Jack Heart) (Stack [Card Jack Heart,Card Ten Spade,Card Nine Heart,Card Eight Spade,Card Seven Heart]) 4 6)
*GameController> (_, g15) = makeMove g14 (MoveCasToCas (Card Six Spade) 4 6)
*GameController> (_, g16) = makeMove g15 (MoveMult (Card Queen Diamond) (Stack [Card Queen Diamond,Card Jack Club]) 1 4)
*GameController> (_, g17) = makeMove g16 (MoveMult (Card Ten Diamond) (Stack [Card Ten Diamond,Card Nine Spade,Card Eight Diamond,Card Seven Spade,Card Six Diamond]) 7 4)
*GameController> (_, g18) = makeMove g17 (MoveCasToCas (Card Seven Diamond) 1 3)
*GameController> (_, g19) = makeMove g18 (MoveCasToFC (Card Jack Diamond) 5)
*GameController> (_, g20) = makeMove g19 (MoveCasToCas (Card Five Heart) 0 6)
*GameController> (_, g21) = makeMove g20 (MoveCasToCas (Card Queen Heart) 1 0)
*GameController> (_, g22) = makeMove g21 (MoveCasToCas (Card Jack Spade) 1 0)
*GameController> (_, g23) = makeMove g22 (MoveMult (Card King Club) (Stack [Card King Club,Card Queen Heart,Card Jack Spade]) 0 1)
*GameController> (_, g24) = makeMove g23 (MoveCasToFC (Card King Heart) 0)
*GameController> (_, g25) = makeMove g24 (MoveCasToFC (Card Nine Club) 0)
*GameController> (_, g26) = makeMove g25 (MoveMult (Card Eight Club) (Stack [Card Eight Club,Card Seven Diamond]) 3 0)
*GameController> (_, g27) = makeMove g26 (MoveCasToCas (Card Ten Heart) 3 1)
*GameController> (stat, g28) = makeMove g27 (MoveCasToFC (Card Nine Diamond) 3)
*GameController> stat
Win
*GameController> g28
FC: 
FD: [13,13,13,13]
1  2  3  4  5  6  7  8 



All Possible Moves: []

```

### Run a solver and print the solutions 

The example of running game number 164. It takes about 13 minutes on 
a dual-core machine.

```
>stack ghc -- -O2 -Wall solver.hs -threaded -rtsopts -eventlog
>./solver +RTS -N2 -s
Please enter a game number: 
164
[MoveCasToCas (Card Six Spade) 0 6,MoveCasToFC (Card Queen Heart) 3,MoveCasToFC (Card Eight Club) 3,MoveCasToFC (Card Nine Club) 3,MoveCasToFC (Card Two Spade) 0,MoveCasToCas (Card Five Heart) 0 6,MoveCasToFoun (Card Ace Diamond) 0,MoveCasToCas (Card Five Spade) 0 1,MoveCasToFoun (Card Ace Spade) 0,MoveCasToFoun (Card Ace Heart) 0,MoveFCToFoun (Card Two Spade),MoveCasToFoun (Card Three Spade) 5,MoveCasToFoun (Card Four Spade) 5,MoveCasToFoun (Card Five Spade) 1,MoveFCToCas (Card Eight Club) 5,MoveMult (Card Seven Diamond) (Stack [Card Seven Diamond,Card Six Spade,Card Five Heart]) 6 5,MoveCasToFC (Card King Spade) 3,MoveCasToCas (Card Eight Heart) 3 0,MoveCasToCas (Card Four Heart) 3 7,MoveCasToFoun (Card Ace Club) 3,MoveMult (Card Five Club) (Stack [Card Five Club,Card Four Heart]) 7 2,MoveFCToCas (Card Nine Club) 3,MoveCasToCas (Card Eight Heart) 0 3,MoveCasToCas (Card Seven Heart) 6 0,MoveCasToFC (Card Six Heart) 1,MoveCasToCas (Card Six Club) 1 0,MoveCasToFC (Card Four Diamond) 7,MoveCasToCas (Card Ten Club) 1 7,MoveCasToFoun (Card Two Heart) 1,MoveCasToFoun (Card Three Heart) 4,MoveCasToFoun (Card Four Heart) 2,MoveCasToFoun (Card Five Heart) 5,MoveCasToFoun (Card Six Spade) 5,MoveFCToFoun (Card Six Heart),MoveMult (Card Six Diamond) (Stack [Card Six Diamond,Card Five Club]) 2 4,MoveFCToCas (Card Four Diamond) 4,MoveCasToFC (Card King Diamond) 2,MoveCasToCas (Card Three Club) 2 4,MoveCasToCas (Card Six Club) 0 5,MoveCasToFoun (Card Seven Heart) 0,MoveCasToFoun (Card Eight Heart) 3,MoveMult (Card Seven Diamond) (Stack [Card Seven Diamond,Card Six Club]) 5 0,MoveCasToFC (Card Nine Club) 3,MoveFCToCas (Card King Diamond) 3,MoveCasToCas (Card Queen Club) 6 3,MoveMult (Card Jack Diamond) (Stack [Card Jack Diamond,Card Ten Club]) 7 3,MoveCasToCas (Card Nine Diamond) 6 3,MoveCasToFoun (Card Two Club) 6,MoveCasToFoun (Card Three Club) 4,MoveCasToFC (Card Jack Club) 7,MoveCasToFoun (Card Two Diamond) 7,MoveCasToFoun (Card Three Diamond) 6,MoveCasToFoun (Card Four Diamond) 4,MoveCasToCas (Card Jack Spade) 1 6,MoveCasToCas (Card Ten Diamond) 1 6,MoveCasToFoun (Card Five Diamond) 1,MoveCasToCas (Card Eight Club) 5 1,MoveCasToFoun (Card Nine Heart) 5,MoveCasToFoun (Card Four Club) 5,MoveCasToFoun (Card Five Club) 4,MoveCasToFoun (Card Six Club) 0,MoveCasToFoun (Card Six Diamond) 4,MoveCasToFoun (Card Seven Diamond) 0,MoveCasToFoun (Card Seven Club) 4,MoveCasToFoun (Card Eight Club) 1,MoveCasToFoun (Card Eight Diamond) 7,MoveCasToFoun (Card Nine Diamond) 3,MoveCasToFoun (Card Ten Diamond) 6,MoveFCToFoun (Card Nine Club),MoveCasToFoun (Card Ten Club) 3,MoveCasToFoun (Card Jack Diamond) 3,MoveFCToFoun (Card Jack Club),MoveCasToFoun (Card Queen Club) 3,MoveCasToCas (Card King Heart) 2 0,MoveCasToFoun (Card Seven Spade) 2,MoveCasToFoun (Card King Club) 2,MoveCasToFoun (Card Eight Spade) 5,MoveCasToFoun (Card Nine Spade) 4,MoveCasToFoun (Card Ten Spade) 4,MoveCasToFoun (Card Ten Heart) 4,MoveCasToFoun (Card Jack Heart) 2,MoveCasToFoun (Card Queen Diamond) 4,MoveCasToFoun (Card King Diamond) 3,MoveCasToFoun (Card Jack Spade) 6,MoveCasToFoun (Card Queen Spade) 5,MoveFCToFoun (Card King Spade),MoveFCToFoun (Card Queen Heart),MoveCasToFoun (Card King Heart) 0]
```
