5
\$\begingroup\$

I am trying to create a Sudoku game in F# and I'm having a bit of trouble with the file reading and writing. But since I've also just started programming in f# I would like to check if my thinking is truly functional or if it have any obvious flaws.

The Sudoku game is made up of 5 parts, Logic.fs with the game board:

let sudokuGame = Array2D.init 9 9 (fun i j -> createSquare i j)

and the Square data structure:

type Square = { x : int; y : int; number : int; optionList : int list }

(x, y being the position x,y in the 2D array, number the accepted value of the Square and optionList the candidates for being the number of the Square), and getter/creation functions for it.

Solver.fs which contains functions to get rows, columns and islands (as lists) that can be matched against the lists in the Squares with a member function. It also has a solver:

let getRow S = 
    let rec getR pos =
        match pos with 
        | 10 -> []
        | x -> (sudokuGame.[(x-1), ((S.y) - 1)]).number :: getR (x+1)
    in getR 1

let getCol S = 
    let rec getC pos =
        match pos with 
        | 10 -> []
        | y -> (sudokuGame.[(S.x - 1), (y - 1)]).number :: getC (y+1)
    in getC 1

let getIsland S = 

    let getX =
        match S.x with
        | x when x < 4 -> 0
        | x when x < 7 -> 3
        | x            -> 6

    let getY = 
        match S.y with
        | y when y < 4 -> 0
        | y when y < 7 -> 3
        | y            -> 6
        List.ofArray [| for x in 0 .. 2 do for y in 0 .. 2 -> sudokuGame.[getX+x,getY+y].number;  |]

//placeInX is a function which leaves the items in x not found in y
let updateOptionList L S = { x= S.x; y=S.y; number = S.number; optionList = (placeInX (S.optionList) L )}

let getUpdatedSquare S =
    if List.length (getSquareList S) <> 0
    then   
        updateOptionList (getRow S) S |> updateOptionList (getCol S) |> updateOptionList (getIsland S) |> createCompleteSquare
    else
        S  

let sodukoSolver (gameBoard : Square[,]) = 
    Array2D.map (getUpdatedSquare) (gameBoard)

let checkSudoku (gameBoard : Square[,]) (reference : Square[,]) =
    gameBoard = reference

Other than that there is the file manipulation file and two GUI files (which will be continued when the logic of the game is complete).

In the file manipulation file I want to create functions to save a game board or retrieve a game board from a file. There I have a lot of problems figuring out how the code should look since Array2D doesn't seem to be able to be cast as a sequence, also saving a Array2D or list with .ToString() doesn't give all the information I want. A solution that I Think might work that I've tried to use is .map or .mapi but I can't get it to work.

Something that might be good to know is that when all logic is complete I'll try to make the Sudoku into a playable game. The game board will therefor be passed in a (theoretically endless) recursive loop.

Can you find any obvious flaws? Any logic or choice of data structures that should be changed or that I should think about? Should I use array2d or would lists, sequences or standard arrays better fit my needs? Are there any functions I've written so far that just doesn't make any sense and should be changed?

I would rather skip any mutables since I want to learn functional programming. So far I used array2d for it's indexing and without using its mutable trait. I also used a global gameBoard even though I would like to be able to load new games from files. To be truly functional, should I remove as many or all global values?

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

Array2D is a bit deficient in terms of basic operations, especially compared to Array, List, Set, and so on. For writing to a file, I would suggest using Array2D.iter and writing each record out in turn; the format that you choose to do this in is up to you. Then reading in each record is as simple as doing an Array2D.init, and taking each record from the file in-turn.

I can't see any obvious flaws here. Perhaps you might want to store the possible values using the built-in Set data-type.

Your code is functional enough, but you will gain more flexibility if you pass around values instead of using a global.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Welcome to CodeReview, I love when the first post is an answer \$\endgroup\$
    – Caridorc
    Sep 5, 2015 at 15:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.