Tetris in F#, dotnet core and MonoGame. Arcade sounds included!
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.
.gitignore Added some basic files, and the start of a project Jun 18, 2018
Program.fs Added necessary bits for monogame integration Jun 21, 2018



Classic tetris, implemented in F# with MonoGame. An exercise in functional programming and game solution design.


Supported platforms

Being dotnet core 2.1, it should work on all platforms that supports (Windows, Linux, Mac). Tested (and largely coded on) Windows 10. A full list of dotnet core supported platforms can be found from here: https://github.com/dotnet/core/blob/master/release-notes/2.1/2.1-supported-os.md

I built this using VS Code, but have also tested opening and running on Visual Studio 2017.

A note for mac users: part of the compilation of this game involves building the content, done using a MonoGame content builder referenced via Nuget. On OSX, this component does not work with just dotnet core. I have managed to get it going by doing the following:

After the build succeeded, a sudo dotnet run started the game without issue.


The game and code is Unilicense, but I have used two sets of external resources:

Unfortunately I got this sound set a long time ago (five plus years) and can't find the exact provenance.

Guide to components

The five code files in this project are described below, in decreasing game importance. Aside from code, there is also the Content folder which contains images, sounds, fonts etc and the Content.mgcb file, which MonoGame uses to compile assets into the game exe. This compilation is triggered by a line in the Tetris.fsproj file (MonoGameContentReference Include="***.mgcb") and done by the builder referenced as a nuget package (MonoGame.Content.Builder). Should be automatic on build (though sometimes you need to build twice).


This file contains a pure F# representation of Tetris: types and DUs to represent the current game state, and functions to transition from one state to the next. It knows nothing of MonoGame, of inputs, resolutions or views - even Colour is an enum defined in Model.fs (which is later translated by View.fs)

The top of the file contains constants for the game, like score amounts, the game width, shape templates etc. The bulk of the file contains methods for transitioning different parts of the state, like processCommand and drop, while the final method AdvanceGame is the primary point of entry. This final method takes the previous/current state, an optional command (translated from Keys by Controller.fs) and an elapsed game time, and returns a new state.


The controller's job is to control when the Model should be advanced, and to feed inputs into it. Primarily, if the game isnt over it will map user keys to Model commands, and pass through the current game time to the controller. Ultimately, the tiny Controller.fs file could be buried in Model.fs if it wasnt for a desire to keep Model.fs purity and hide the MonoGame keys construct. By splitting these out, how the game is controlled becomes an abstraction: blocks could be rotated or moved with the mouse if necessary, by modifying Controller.fs alone.


If Controller.fs was about inputs, then View.fs is about outputs. It takes a model, and translates it to rendered sprites on the screen and sounds to be played. Despite this job, it is still largely agnostic of XNA, translating the model to abstractions which the GameLoop then uses. It is aware of the MonoGame colour construct, however, as mentioned.


GameCore is where all the MonoGame stuff is kept, and is basically the MonoGame.Game game loop class plus a bunch of abstraction types used to keep the other files pure. MonoGame is a class-based, imperative game development framework, that needs mutable types aplenty to work, and in order to keep my game largely free of these impurities, its all buried in here. GameLoop, the key class, is given the entry methods and model type from the other files, and orchestrates them. It could be viewed as the Imperative shell of the game.


This instantiates the GameLoop, passing through the methods and types from the other files, and handles disposal


There is a branch of this project called corert, that has CoreRT enabled. This has been tested to build on Windows, if you have the necessary requisites installed. Feel free to try it, but for support on getting it to build on Windows or Linux/OSX you will need to seek help at the CoreRT site.

Note on development sequence

This project was the first developed after Battleship here.

The next project developed after this, and using the lessons learned, was MiniKnight here.