Skip to content
This repository was archived by the owner on Mar 27, 2025. It is now read-only.

Update exercises #126

Merged
merged 14 commits into from
Sep 7, 2023
Merged
5 changes: 5 additions & 0 deletions exercises/exercise_000_sudoku_solver_initial_state/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ in place in this project. scalafmt supports both Scala 2 and Scala 3. You can
Scala 2 to Scala 3, you need to make sure that a matching scalafmt configuration is
in place.

### Next steps

After successfully completing the tasks in this exercise, move to the next one by
running the `cmtc next-exercise` from the command line.

### Markdown viewer in IntelliJ IDEA

The font size can be a bit too small for the taste of some people. You can change the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,14 @@ wildcard import syntax from using an asterix (`*`) instead of an underscore (`_`
changes.
- The end result should be that, after the compiler has applied its rewrites, the source code
compiles clean.
- Remove the `-rewrite` from `scalacOptions` in the sbt build definition.
- Checkpoint the current state of your code by commiting the changes to git:

```scala
$ git commit -a -m "Snapshot after Scala 3 compiler syntax rewrites"
```

- Move to the next exercise by running the `cmtc next-exercise` command from the command line.

> NOTE: The extra bit of code that was added via `cmtc pull-template ...` can either be left as-is
> or be removed. Your choice.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,23 @@ one came from, the formatting may be different, but semantically equivalent).
val rewriteOldSyntax = Seq("-rewrite", "-old-syntax")
```

- Before proceeding, let's take a snapshot of the current state of the exercises
by executing the following commands:
- If you didn't take a snapshot by committing the code changes at the end of the
previous exercise, do so now by executing the following command:

```scala
$ git add -A
$ git commit -m "Snapshot before Scala 3 compiler syntax rewrites"
$ git commit -a -m "Snapshot before Scala 3 compiler syntax rewrites"
```

You can now have the compiler rewrite the source code to switch to one of the
alternative syntax options.

- The values mentioned above each contain a specific set of compiler options
for a specific syntax rewrite.
- Note that syntax rewrites have to be executed one at a time. Also, consecutive
syntax rewrites have to be executed in a certain order. Make sure you understand
what's explained in the section named `Setting and Rewrites` at the end of the
[Optional Braces](https://dotty.epfl.ch/docs/reference/other-new-features/indentation.html)
section in the Scala 3 reference documentation.

- Now go through the following sequence of actions:
- Add one of the syntax rewrite values to the compiler option section.
Expand All @@ -62,6 +66,15 @@ documentation.
> For the remainder of the exercises in this course, we will use the New Control
> Structure syntax and the Fewer Braces syntax.

- Checkpoint the current state of the code by executing the following command:

```scala
$ git commit -a -m "Snapshot after Scala 3 compiler syntax rewrites"
```

- You've just completed this exercise. Let's move on to the next exercise by
executing the `cmtc next-exercise` command.

## Source code formatting & Markdown viewer in IntelliJ

### Source code formatting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ object ReductionRules:
}

val cellIndexesToValues =
CellPossibleValues.zip(valueOccurrences).groupBy { case (value, occurrence) => occurrence }.filter {
case (loc, occ) => loc.length == occ.length && loc.length <= 6
}
CellPossibleValues
.zip(valueOccurrences)
.groupBy { case (value, occurrence) => occurrence }
.filter:
case (loc, occ) => loc.length == occ.length && loc.length <= 6

val cellIndexListToReducedValue = cellIndexesToValues.map { case (index, seq) =>
(index, seq.map { case (value, _) => value }.toSet)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class SudokuDetailProcessor[DetailType <: SudokuDetailType: SudokuDetailProcesso
import SudokuDetailProcessor.*

def operational(id: Int, state: ReductionSet, fullyReduced: Boolean): Behavior[Command] =
Behaviors.receiveMessage {
Behaviors.receiveMessage:
case Update(cellUpdates, replyTo) if !fullyReduced =>
val previousState = state
val updatedState = mergeState(state, cellUpdates)
Expand Down Expand Up @@ -78,21 +78,21 @@ class SudokuDetailProcessor[DetailType <: SudokuDetailType: SudokuDetailProcesso
case ResetSudokuDetailState =>
operational(id, InitialDetailState, fullyReduced = false)

}

private def mergeState(state: ReductionSet, cellUpdates: CellUpdates): ReductionSet =
cellUpdates.foldLeft(state) { case (stateTally, (index, updatedCellContent)) =>
stateTally.updated(index, stateTally(index) & updatedCellContent)
}

private def stateChanges(state: ReductionSet, updatedState: ReductionSet): CellUpdates =
state.zip(updatedState).zipWithIndex.foldRight(cellUpdatesEmpty) {
case (((previousCellContent, updatedCellContent), index), cellUpdates)
if updatedCellContent != previousCellContent =>
(index, updatedCellContent) +: cellUpdates

case (_, cellUpdates) => cellUpdates
}
state
.zip(updatedState)
.zipWithIndex
.foldRight(cellUpdatesEmpty):
case (((previousCellContent, updatedCellContent), index), cellUpdates)
if updatedCellContent != previousCellContent =>
(index, updatedCellContent) +: cellUpdates

case (_, cellUpdates) => cellUpdates

private def isFullyReduced(state: ReductionSet): Boolean =
val allValuesInState = state.flatten
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,10 @@ object SudokuIO:
def convertFromCellsToComplete(cellsIn: Vector[(String, Int)]): Vector[(Int, CellUpdates)] =
for
(rowCells, row) <- cellsIn
updates = rowCells.zipWithIndex.foldLeft(cellUpdatesEmpty) {
updates = rowCells.zipWithIndex.foldLeft(cellUpdatesEmpty):
case (cellUpdates, (c, index)) if c != ' ' =>
(index, Set(c.toString.toInt)) +: cellUpdates
case (cellUpdates, _) => cellUpdates
}
yield (row, updates)

def readSudokuFromFile(sudokuInputFile: java.io.File): Vector[(Int, CellUpdates)] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class SudokuProblemSender private (
) // on a 5 node RPi 4 based cluster in steady state, this can be lowered to about 6ms

def sending(): Behavior[Command] =
Behaviors.receiveMessage {
Behaviors.receiveMessage:
case SendNewSudoku =>
context.log.debug("sending new sudoku problem")
val nextRowUpdates = rowUpdatesSeq.next()
Expand All @@ -83,4 +83,3 @@ class SudokuProblemSender private (
case SolutionWrapper(solution: SudokuSolver.SudokuSolution) =>
context.log.info(s"${SudokuIO.sudokuPrinter(solution)}")
Behaviors.same
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class SudokuProgressTracker private (
import SudokuProgressTracker.*

def trackProgress(updatesInFlight: Int): Behavior[Command] =
Behaviors.receiveMessage {
Behaviors.receiveMessage:
case NewUpdatesInFlight(updateCount) if updatesInFlight - 1 == 0 =>
context.log.debug("NewUpdatesInFlight({}) - UpdatesInFlight={}", updateCount, updatesInFlight + updateCount)
rowDetailProcessors.foreach { case (_, processor) =>
Expand All @@ -40,12 +40,11 @@ class SudokuProgressTracker private (
case msg: SudokuDetailState =>
context.log.error("Received unexpected message in state 'trackProgress': {}", msg)
Behaviors.same
}

def collectEndState(
remainingRows: Int = 9,
endState: Vector[SudokuDetailState] = Vector.empty[SudokuDetailState]): Behavior[Command] =
Behaviors.receiveMessage {
Behaviors.receiveMessage:
case detail: SudokuDetailState if remainingRows == 1 =>
sudokuSolver ! Result((detail +: endState).sortBy { case SudokuDetailState(idx, _) => idx }.map {
case SudokuDetailState(_, state) => state
Expand All @@ -56,4 +55,3 @@ class SudokuProgressTracker private (
case msg: NewUpdatesInFlight =>
context.log.error("Received unexpected message in state 'collectEndState': {}", msg)
Behaviors.same
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,12 @@ object SudokuSolver:

def apply(sudokuSolverSettings: SudokuSolverSettings): Behavior[Command] =
Behaviors
.supervise[Command] {
.supervise[Command]:
Behaviors.withStash(capacity = sudokuSolverSettings.SudokuSolver.StashBufferSize) { buffer =>
Behaviors.setup { context =>
new SudokuSolver(context, buffer).idle()
}
}
}
.onFailure[Exception](
SupervisorStrategy.restartWithBackoff(minBackoff = 5.seconds, maxBackoff = 1.minute, randomFactor = 0.2))

Expand All @@ -66,7 +65,7 @@ class SudokuSolver private (context: ActorContext[SudokuSolver.Command], buffer:
context.spawn(SudokuProgressTracker(rowDetailProcessors, progressTrackerResponseMapper), "sudoku-progress-tracker")

def idle(): Behavior[Command] =
Behaviors.receiveMessage {
Behaviors.receiveMessage:

case InitialRowUpdates(rowUpdates, sender) =>
rowUpdates.foreach { case SudokuDetailProcessor.RowUpdate(row, cellUpdates) =>
Expand All @@ -79,10 +78,8 @@ class SudokuSolver private (context: ActorContext[SudokuSolver.Command], buffer:
context.log.error("Received an unexpected message in 'idle' state: {}", unexpectedMsg)
Behaviors.same

}

def processRequest(requestor: Option[ActorRef[Response]], startTime: Long): Behavior[Command] =
Behaviors.receiveMessage {
Behaviors.receiveMessage:
case SudokuDetailProcessorResponseWrapped(response) =>
response match
case SudokuDetailProcessor.RowUpdate(rowNr, updates) =>
Expand Down Expand Up @@ -184,7 +181,6 @@ class SudokuSolver private (context: ActorContext[SudokuSolver.Command], buffer:
case msg: InitialRowUpdates =>
buffer.stash(msg)
Behaviors.same
}

private def resetAllDetailProcessors(): Unit =
for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,32 @@ import org.lunatechlabs.dotty.sudoku.CellMappings.*

class CellMappingSuite extends munit.FunSuite:

test("Mapping row coordinates should result in correct column & block coordinates") {
assertEquals(rowToColumnCoordinates(0, 0), ((0, 0)))
assertEquals(rowToBlockCoordinates(0, 0), ((0, 0)))
assertEquals(rowToColumnCoordinates(8, 8), ((8, 8)))
assertEquals(rowToBlockCoordinates(8, 8), ((8, 8)))
assertEquals(rowToColumnCoordinates(3, 4), ((4, 3)))
assertEquals(rowToBlockCoordinates(3, 4), ((4, 1)))
assertEquals(rowToBlockCoordinates(4, 3), ((4, 3)))
}
test("Mapping row coordinates should result in correct column & block coordinates"):
assertEquals(rowToColumnCoordinates(0, 0), ((0, 0)))
assertEquals(rowToBlockCoordinates(0, 0), ((0, 0)))
assertEquals(rowToColumnCoordinates(8, 8), ((8, 8)))
assertEquals(rowToBlockCoordinates(8, 8), ((8, 8)))
assertEquals(rowToColumnCoordinates(3, 4), ((4, 3)))
assertEquals(rowToBlockCoordinates(3, 4), ((4, 1)))
assertEquals(rowToBlockCoordinates(4, 3), ((4, 3)))

test("Mapping column coordinates should result in correct row & block coordinates") {
assertEquals(columnToRowCoordinates(0, 0), ((0, 0)))
assertEquals(columnToBlockCoordinates(0, 0), ((0, 0)))
assertEquals(columnToRowCoordinates(8, 8), ((8, 8)))
assertEquals(columnToBlockCoordinates(8, 8), ((8, 8)))
assertEquals(columnToRowCoordinates(3, 4), ((4, 3)))
assertEquals(columnToBlockCoordinates(3, 4), ((4, 3)))
assertEquals(columnToBlockCoordinates(4, 3), ((4, 1)))
}
test("Mapping column coordinates should result in correct row & block coordinates"):
assertEquals(columnToRowCoordinates(0, 0), ((0, 0)))
assertEquals(columnToBlockCoordinates(0, 0), ((0, 0)))
assertEquals(columnToRowCoordinates(8, 8), ((8, 8)))
assertEquals(columnToBlockCoordinates(8, 8), ((8, 8)))
assertEquals(columnToRowCoordinates(3, 4), ((4, 3)))
assertEquals(columnToBlockCoordinates(3, 4), ((4, 3)))
assertEquals(columnToBlockCoordinates(4, 3), ((4, 1)))

test("Mapping block coordinates should result in correct row & column coordinates") {
assertEquals(blockToRowCoordinates(0, 0), ((0, 0)))
assertEquals(blockToColumnCoordinates(0, 0), ((0, 0)))
assertEquals(blockToRowCoordinates(8, 8), ((8, 8)))
assertEquals(blockToColumnCoordinates(8, 8), ((8, 8)))
assertEquals(blockToRowCoordinates(4, 3), ((4, 3)))
assertEquals(blockToColumnCoordinates(4, 3), ((3, 4)))
assertEquals(blockToRowCoordinates(3, 4), ((4, 1)))
assertEquals(blockToColumnCoordinates(3, 4), ((1, 4)))
assertEquals(blockToRowCoordinates(5, 5), ((4, 8)))
assertEquals(blockToColumnCoordinates(5, 5), ((8, 4)))
}
test("Mapping block coordinates should result in correct row & column coordinates"):
assertEquals(blockToRowCoordinates(0, 0), ((0, 0)))
assertEquals(blockToColumnCoordinates(0, 0), ((0, 0)))
assertEquals(blockToRowCoordinates(8, 8), ((8, 8)))
assertEquals(blockToColumnCoordinates(8, 8), ((8, 8)))
assertEquals(blockToRowCoordinates(4, 3), ((4, 3)))
assertEquals(blockToColumnCoordinates(4, 3), ((3, 4)))
assertEquals(blockToRowCoordinates(3, 4), ((4, 1)))
assertEquals(blockToColumnCoordinates(3, 4), ((1, 4)))
assertEquals(blockToRowCoordinates(5, 5), ((4, 8)))
assertEquals(blockToColumnCoordinates(5, 5), ((8, 4)))
Loading