Skip to content

Commit

Permalink
rock paper scissors
Browse files Browse the repository at this point in the history
  • Loading branch information
nmheim committed Apr 18, 2024
1 parent 8ac1dec commit ab9dd8b
Show file tree
Hide file tree
Showing 5 changed files with 275 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export default defineConfig({
{ text: '9. Least Common Anchestor', link: '/exams/least-common-ancestor/' },
{ text: '10. Building Trees', link: '/exams/building-trees/' },
{ text: '11. Square Code', link: '/exams/square-code/' },
// { text: '12. Rock, Paper, Scissors', link: '/exams/filetree' },
{ text: '12. Rock, Paper, Scissors', link: '/exams/rock-paper-scissors/' },
// { text: '13. Sierpinski Carpet', link: '/exams/filetree' },
// { text: '14. Spiral Matrix', link: '/exams/filetree' },
// { text: '15. Unit Propagation', link: '/exams/filetree' },
Expand Down
203 changes: 203 additions & 0 deletions exams/rock-paper-scissors/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
---
title: "Exam Task: Multiplayer *Rock, Paper, Scissors*"
subtitle: "From: Exam 2023-06-19"
outline: deep
---

# Multiplayer *Rock, Paper, Scissors*

Determine the winner (or remaining players) after several rounds of multiplayer *Rock, Paper, Scissors*.

Players stand in a circle and all throw at once. If rock, paper, and scissors are all thrown, it is
a stalemate, and they rethrow. If only two throws are present, all players with the losing throw are
eliminated. The following function decides which throws from a set of set of *throws* in a
single round should be eliminated.
$$
\text{eliminated}(\text{throws}) \equiv \begin{cases}
\texttt{rock} & \texttt{paper}, \texttt{rock} = \text{throws} \\
\texttt{paper} & \texttt{paper}, \texttt{scissors} = \text{throws} \\
\texttt{scissors} & \texttt{rock}, \texttt{scissors} = \text{throws} \\
\emptyset &\text{otherwise}
\end{cases}
$$

The actions of players are decided in advance; e.g. for two rounds of three-player RPS, the players
and actions are represented as:
```scheme
(define players '("alice" "bob" "charlie"))
(define strategies '((r p) (r r) (s p)))
```
where Alice throws Rock in the first round, then Paper in the second. Charlie never gets to throw
his second pick (Paper), as he will be eliminated after one round. Alice is the only remaining
player after the two rounds. The result should be `'("alice")`.

You will need to keep track of the players and their actions. In each round, figure out which throws
should be eliminated, then filter out the corresponding players and their strategies. Create two
helper functions: one that creates a boolean "mask" of all the winners of a single round, and one
that performs the filtering.

## Racket
Throws will be represented as the symbols `'r`, `'p` and `'s`.
```scheme
#lang racket
(provide rps)
(define (game-finished? strats) (or (null? strats) (ormap null? strats)))
(define (strats-current strats) (map car strats))
(define (strats-future strats) (map cdr strats))
(define (rps players strategies)
(if (game-finished? strategies)
players
(let* ((current (strats-current strategies))
(future (strats-future strategies)))
; Implement me!
)
)
```
You can use the `remove-duplicates` function to remove duplicate throws and `sort` with `symbol<?` to order throws.

### Examples

Alice wins in the second round. Charlie loses immediately.
```scheme
(define players '("alice" "bob" "charlie"))
(define strategies '((r p) (r r) (s p)))
(rps players strategies); '("alice")
```

Charlie loses because rock beats scissors (single round).
```scheme
(define players '("alice" "bob" "charlie"))
(define strategies '((r) (r) (s)))
(rps players strategies); '("alice" "bob")
```

First two rounds are stalemates (stalemate, stalemate, win).
```scheme
(define players '("alice" "bob" "charlie"))
(define strategies '((r p r) (p s r) (s r p)))
(rps players strategies) ; '("charlie")
```

::: details
```scheme
#lang racket
(provide rps)
(define players '("alice" "bob" "charlie"))
(define strategies '((r p) (r r) (s p)))
(define (game-finished? strats) (andmap null? strats))
(define (strats-current strats) (map car strats))
(define (strats-future strats) (map cdr strats))
(define (view xs bools)
(for/list ((x xs) (b bools) #:when b) x))
(define (eliminated-throw throws)
(match (remove-duplicates (sort throws symbol<?))
('(p r) 'r)
('(p s) 'p)
('(r s) 's)
(_ '())))
(define (rps-survivors throws)
(let* ((elim (eliminated-throw throws)))
(map (curry (compose not equal?) elim) throws)))
(define (rps players strategies)
(if (game-finished? strategies)
players
(let* ((current (strats-current strategies))
(future (strats-future strategies))
(mask (rps-survivors current)))
(rps (view players mask) (view future mask)))))
```
:::

## Haskell

For simplicity, throws will be represented as the characters `'r'`, `'p'` and `'s'`.
```haskell
module Task4 (rps) where
import Data.List

isFinished xs = null xs || any null xs

currentStrategies = map head

futureStrategies = map tail

rps :: [String] -> [[Char]] -> [String]
rps players strategies | isFinished strategies = players
rps players strategies =
let current = currentStrategies strategies
future = futureStrategies strategies
in [] -- Implement me !
```
You can use the `nub` function to remove duplicate throws and `sort` to order the throws.

### Examples

Alice wins in the second round. Charlie loses immediately.
```haskell
players = ["alice", "bob", "charlie"]
strategies = [['r', 'p'], ['r', 'r'], ['s', 'p']]

rps players strategies -- ["alice"]
```


Charlie loses because rock beats scissors (single round).
```haskell
players = ["alice", "bob", "charlie"]
strategies = [['r'], ['r'], ['s']]

rps players strategies -- ["alice", "bob"]
```


First two rounds are stalemates (stalemate, stalemate, win).
```haskell
players = ["alice", "bob", "charlie"]
strategies = [['r', 'p', 'r'], ['p', 's', 'r'], ['s', 'r', 'p']]

rps players strategies -- ["charlie"]
```

::: details
```haskell
module Task4 (rps) where
import Data.List

isFinished xs = null xs || any null xs

currentStrategies = map head

futureStrategies = map tail

view xs bs = map fst $ filter snd $ zip xs bs

eliminated throws =
let e = elim $ sort $ nub throws
in map (/= e) throws
where
elim ['p', 'r'] = 'r'
elim ['p', 's'] = 'p'
elim ['r', 's'] = 's'
elim _ = ' '

rps :: [String] -> [[Char]] -> [String]
rps players strategies | isFinished strategies = players
rps players strategies =
let current = currentStrategies strategies
future = futureStrategies strategies
mask = eliminated current
in rps (view players mask) (view future mask)
```
:::
Binary file added exams/rock-paper-scissors/main_en.pdf
Binary file not shown.
44 changes: 44 additions & 0 deletions exams/rock-paper-scissors/task3.rkt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#lang racket
(provide rps)

; ---------------------------------------------------------------------------------------------------
; Multiplayer Rock Paper Scisors:
; Determine the winner(s) after several rounds of multiplayer Rock Paper Scissors.

; Rules of multiplayer RPS: Players stand in a circle and all throw at once.
; If rock, paper, and scissors are all thrown, it is a stalemate, and they rethrow. If only two
; throws are present, all players with the losing throw are eliminated.

; As functional programmers, we believe in determinism :). The strategies of players are known in
; advance; e.g. for two rounds of three-player RPS, the players and actions are represented:
(define players '("alice" "bob" "charlie"))
(define strategies '((r p) (r r) (s p)))
; where Alice throws Rock in the first round, then Paper in the second. Charlie never gets to
; throw his second pick, as he will be eliminated after one round. Alice is the winner.
; ---------------------------------------------------------------------------------------------------

(define (game-finished? strats) (andmap null? strats))
(define (strats-current strats) (map car strats))
(define (strats-future strats) (map cdr strats))

(define (view xs bools)
(for/list ((x xs) (b bools) #:when b) x))

(define (eliminated-throw throws)
(match (remove-duplicates (sort throws symbol<?))
('(p r) 'r)
('(p s) 'p)
('(r s) 's)
(_ '())))

(define (rps-survivors throws)
(let* ((elim (eliminated-throw throws)))
(map (curry (compose not equal?) elim) throws)))

(define (rps players strategies)
(if (game-finished? strategies)
players
(let* ((current (strats-current strategies))
(future (strats-future strategies))
(mask (rps-survivors current)))
(rps (view players mask) (view future mask)))))
27 changes: 27 additions & 0 deletions exams/rock-paper-scissors/task4.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module Task4 (rps) where
import Data.List

isFinished xs = null xs || any null xs

currentStrategies = map head

futureStrategies = map tail

view xs bs = map fst $ filter snd $ zip xs bs

eliminated throws =
let e = elim $ sort $ nub throws
in map (/= e) throws
where
elim ['p', 'r'] = 'r'
elim ['p', 's'] = 'p'
elim ['r', 's'] = 's'
elim _ = ' '

rps :: [String] -> [[Char]] -> [String]
rps players strategies | isFinished strategies = players
rps players strategies =
let current = currentStrategies strategies
future = futureStrategies strategies
mask = eliminated current
in rps (view players mask) (view future mask)

0 comments on commit ab9dd8b

Please sign in to comment.