CS463 - Homework 9 - Game of Functional Thrones

Due Tuesday, April 23 2024, 5:00pm

Table of Contents

Quick Overview

  • We expect identical output to last time, so we will even use the same consistency checker script: chairs_checker.py
  • you may work with one partner this time.
    • You may share everything except the writeup.
    • Each Turn In Your Work. This is primarily as a backup in case your partner fails to turn things in appropriately for you, or someone wants to work into the late period.
      • Please explain in your writeup how similar your work is to your partner's so we can speed up grading.
  • start with the Required Components to begin your file.

Suggested Resources

Look at our class examples on concurrency in Haskell for a good starting point. If you're unsure what approach to use, I'd suggest using simple MVar's, and perhaps some Chan's as you see fit. If you want some more longform introduction, look to our readings on the schedule covering Monads/IO (LYAHFGG, RWH). When you want to find a built-in function, type in the signature to hoogle and see if you can find an existing definition. In contrast, I'd strongly suggest not using the STM monad (software transactional memory), as it's a lot harder to get correct, and mixing it with the other monads is a much more complex undertaking than our class has prepared your for.

Description / Review

We are re-implementing our Musical Chairs assignment in Haskell. We will most likely use the MVar, Chan, and/or BoundedChan types; we will create threads for players and the emcee (via forkIO or async), just as before; and chairs need to be separate resources (e.g. different MVar's or some other shared things), just as before. We will target the same output as before, too. In short, use Haskell to conform to the same rules as in the previous musical chairs assignment.

Honestly, that should be about enough to define it. But we will review what the layout is, and then give some Haskell-specific suggestions to help you get started.

The Task: Musical Chairs

N players want to sit on (N-1) chairs. An emcee (the announcer) turns the music on and off, and comments on who wins, who loses, and so on. Each round, when the music turns off, all active players try to sit in the chairs, but one will not be able to find a chair. The emcee announces that that player has lost, and the next round begins with one fewer chairs and one fewer players, until there is one winner.

Turning it in

  1. All Haskell implementations need to have a Homework9.hs module with the main method to run your entire program.
  2. Create a folder named with this convention:
    gmason76_h9/
    

    Inside of it, place the following:

    • all source code (COMMENTED!)
    • README.txt (your write-up; other common document types accepted)
    • any (pre-approved) modules your code needs outside of the standard library (such as BoundedChan).
  3. zip that folder, and submit it on GradeScope. Yes, you and your partner both turn it in separately.

Getting Help

Threaded programing can be surprising when we first start. Please begin the assignment early enough that you can ask questions.

Spend more time than you might be used to spend planning your algorithm, thinking through any potential issues, and really mapping out your plans. Don't just code-as-you-go and hope you stumble into a solution.

Also, think about good early milestone versions of your program that can help you ensure certain features or behaviors of your code are working before you tackle the full program.

Just a reminder, collaboration on the assignment with one partner is allowed; you can discuss concurrency issues generally with anyone, but once you're working on musical chairs (either implementation details or thinking through how to solve it in general), you need to restrict yourself to just you, your partner, and the instructors of the course (GTA, professor).

Grading

Points Breakdown:

category points
Structure 50
Performance 35
Write-up 15
Total 100

Structure: 50

These points go towards the structure and approach of your code. You still need to have compiling, runnable code that is mostly complete in order to earn all these points, but it's possible to get some points here even if you have some later issues.

  • +10 - emcee runs the show via stopping music
    • just means that the player threads read some variable to begin seeking chairs. (Hint: perhaps an MVar?)
    • you need to mention this in your writeup for full credit.
  • +10 - each player is its own thread
    • find the map/recursion/whatever that creates them, and ensure that this code actually performs the playing task.
    • note: forkIO is how we create/start threads in Haskell.
  • +10 - each chair is its own resource
    • e.g., NOT just a list of ints, or a single int like numChairsLeft. There should be some linear structure holding all the individual chair resources, which is still okay.
    • you need to mention your approach in your writeup for full credit.
  • +10 - multiple chairs can be accessed at the same time (there is no "global lock").
  • + 5 - surviving players preserved for next round
    • loser thread is finished/killed, or somehow removed from gameplay while all others continue to the next round, rather than restarting an entire cast of player threads. Creating more player threads is not allowed for successive rounds, even if their player numbers are preserved!
  • + 5 - handles variable # of players without crashing due to the number of players value. (not hard-coded to ten players).

Performance: 35

These points are earned by the functionality and behavior of your code.

  • +15: various sizes successfully complete the game. (E.g. different N values work).
  • +10: specific sizes repeatedly complete the game in different ways (E.g. repeated runs of N = 5 show different valid results).
  • +10: all printouts are precisely as required
    • especially important: no extra printing in your submitted work beyond the expected messages. Comment out those debugging statements!

Writeup: 15

  • three points per question, for the first five questions. (#6 is optional).

Common Deductions

  • structure:
    • poorly- or un-commented code. (up to -10)
    • forgot to mention line numbers of emcee stopping music, where threads are created, or chairs as resources.
    • there exists some bottleneck that all chair-acquisition attempts must go through (some "global lock" that becomes a one-at-a-time request).
  • performance:
    • extra printing other than the required print statements. clean up your code before submitting! (up to -10)
    • mis-ordered print statements, e.g. sat messages not all received/printed before the next round begins, or a lost message that isn't the last message of the round. (up to -10)
    • program sometimes, often, or always hangs/crashes without completing the game. (much of the performance points are at stake in this situation)
  • Writeup:
    • skipped parts of question or didn't answer what was asked.

Programming Hints

  • We studied a few different approaches in class:
    • Control.Concurrent.MVar provides operations takeMVar and putMVar to get a single-slot, automatically blocking buffer. It also offers non-blocking versions tryPutMVar and tryTakeMVar.
    • Control.Concurrent.MChan provides unbounded space for values to be placed/removed via newChan, writeChan, and readChan.
    • BoundedChan provides the same notion of MChan, but at a fixed size (after which it exhibits blocking behavior). We likely don't need the bounding to get a semantically correct implementation but it may fit your idea better.
  • think about how you can create a piece and test it individually; do you have a simpler mini-program you'd like to get working, first? Something you can typecheck and run and learn a bit from, and then perhaps directly adapt into the assignment code. (I don't have a secret example in mind but you can often simplify a program and get the same style of code).
  • use some sort of centralized authority for printing, like the announcer in our class examples. It was the sole consumer of an MVar String, where all other threads could produce values for the MVar. Although this announcer's job is to putStrLn various messages, the MVar doesn't necessarily have to hold strings - perhaps your own custom datatype could be used here.
  • Though there is an interesting Software Transactional Memory library (providing TMVar, TVar, TChan), it's not really interoperable with our main class examples. I strongly recommend that you don't try to use it on the assignment, and definitely not mixed with the regular MVar / IORef / Chan concurrency primitives.

Multicore usage in Haskell

Tip on using more logical cores in Haskell concurrent code

(This content is from our "installing haskell" text document, but is finally relevant here).

Haskell has a way to specify how many cores you'd like your code to run; these are called "Haskell Executable Contexts" (HECs).

 ghc -threaded -main-is Homework9 -o h9 Homework9.hs
./h9 10 +RTS -N4 -RTS

ghc: We first want to compile our code with ghc.

  • -threaded: multithreading, please
  • -main-is Homework9: the main function we want to run isn't in a module named Main, so we specify where that main function is.
  • -o h9: the output should be named h9.
  • Homework9.hs: here's the source code.

Then, we run the executable:

  • ./h9: this thing should be executed. (I'm running on mac and on zeus; on windows, likely you'd just have h9).
  • 10: a normal command line argument.
  • +RTS ... -RTS: between these is where you can pass "runtime system" arguments.
  • -N4: the number of HECs should be four, please.

I've run this successfully on my own machine (mac) and on zeus.

If you don't use this approach, it's likely that the way your code's logical threads get scheduled may be a bit more (too) predictable, but it's not something you have to do to run haskell code. On large enough examples, e.g. N=5 or more, you should start seeing a bit of variability.