Compare commits

...

11 Commits

Author SHA1 Message Date
37314cf9db added Snowflake 2024-04-30 13:22:57 +02:00
e6ec271afd added NaturalTree 2024-04-30 13:22:57 +02:00
0e46e1a292 added Tree 2024-04-30 13:22:57 +02:00
11c90daaa2 added drawCircle 2024-04-30 13:22:56 +02:00
a280dfd564 added TestLogo 2024-04-30 13:22:56 +02:00
9d6b1f767e implemented grid of size n 2024-04-30 13:22:56 +02:00
7141534e9f added TextDisplay and GraphicsDisplay 2024-04-30 13:22:55 +02:00
8e9567e1a2 magic square solver 2024-04-30 13:22:55 +02:00
124b4c7a33 added out to .gitignore 2024-04-30 13:22:47 +02:00
5afec8149d task 1 2024-04-30 13:20:08 +02:00
38c3be5740 initial commit 2024-04-30 13:20:07 +02:00
16 changed files with 519 additions and 0 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
/out/
# ---> Scala # ---> Scala
*.class *.class
*.log *.log

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

6
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/Lab17.iml" filepath="$PROJECT_DIR$/Lab17.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

13
Lab17.iml Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="scala-sdk-2.13.12" level="project" />
<orderEntry type="library" name="FunGraphics-1.5.13" level="application" />
</component>
</module>

32
src/Hanoi.scala Normal file
View File

@@ -0,0 +1,32 @@
import scala.collection.mutable
class Hanoi(val n: Int, val left: Char, val mid: Char, val right: Char) {
val lists: mutable.HashMap[Char, mutable.Stack[Int]] = new mutable.HashMap()
lists(left) = mutable.Stack.range(1, n + 1)
lists(mid) = mutable.Stack.empty
lists(right) = mutable.Stack.empty
def move(from: Char, to: Char): Unit = {
val i: Int = lists(from).pop()
println(s"Moving disk $i from $from->$to")
lists(to).push(i)
}
def solve(): Unit = {
hanoi(n, left, mid, right)
}
private def hanoi(n: Int, start: Char, aux: Char, end: Char): Unit = {
if (n == 0) return
hanoi(n - 1, start, end, aux)
move(start, end)
hanoi(n - 1, aux, start, end)
}
}
object Hanoi {
def main(args: Array[String]): Unit = {
val hanoi: Hanoi = new Hanoi(3, 'A', 'B', 'C')
hanoi.solve()
}
}

View File

@@ -0,0 +1,93 @@
package logo
import java.awt.{Color, Point}
import scala.util.Random
class NaturalTree(width: Int, height: Int, angleStep_ : Double) extends Tree(width, height, angleStep_) {
val MIN_STRAW_ANGLE: Double = 60
val MAX_STRAW_ANGLE: Double = 100
val MIN_N_STRAWS: Int = 90
val MAX_N_STRAWS: Int = 200
//val STRAW_COLOR1: Color = new Color(230, 188, 43)
//val STRAW_COLOR2: Color = new Color(255, 226, 71)
//val STRAW_COLOR1: Color = new Color(96, 163, 67)
//val STRAW_COLOR2: Color = new Color(112, 198, 76)
val STRAW_COLOR1: Color = new Color(108, 83, 61)
val STRAW_COLOR2: Color = new Color(174, 105, 44)
val TRUNK_COLOR: Color = new Color(65, 61, 64)
override def drawTree(n: Int, length: Double): Unit = {
var n2: Int = n
val r: Double = math.random()
if (r < 0.25) {
n2 -= 1
} else if (r > 0.9) {
if (n2 < 2) {
n2 += 3
}
}
turn(angleStep * (math.random() - 0.5) * 2)
val a: Double = angleStep
angleStep -= math.random() * angleStep / 10
super.drawTree(n2, length)
angleStep = a
}
override def drawBranch(length: Double): Unit = {
setPenWidth((length / 10).toFloat)
setColor(TRUNK_COLOR)
super.drawBranch(length)
}
override def drawLeaf(length: Double): Unit = {
val angle: Double = getTurtleAngle()
val pos: Point = getPosition()
val nStraws: Int = Random.between(MIN_N_STRAWS, MAX_N_STRAWS)
val strawsAngle: Double = Random.between(MIN_STRAW_ANGLE, MAX_STRAW_ANGLE)
val step: Double = strawsAngle / (nStraws - 1)
val oAngle: Double = angle - strawsAngle / 2
val strawLen: Double = length * 20
setPenWidth(1f)
for (i: Int <- 0 until nStraws) {
setPenWidth(0.5f + (nStraws - i) / (nStraws - 1f) / 10)
setColor(lerpCol((nStraws - i) / (nStraws - 1.0)))
//val a: Double = oAngle + i * step + (math.random() * 2 -1) * step / 5
val a: Double = oAngle + math.random() * strawsAngle
var l: Double = strawLen * (nStraws.toDouble - i) / nStraws
l += (math.random() * 2 - 1) * l / 2
setAngle(a)
jump(pos.x, pos.y)
//forward(strawLen + (math.random() * 2 - 1) * strawLen / 2)
forward(l)
}
jump(pos.x, pos.y)
setAngle(angle)
}
def lerpCol(f: Double): Color = {
val r: Int = ((STRAW_COLOR2.getRed - STRAW_COLOR1.getRed) * f + STRAW_COLOR1.getRed).toInt
val g: Int = ((STRAW_COLOR2.getGreen - STRAW_COLOR1.getGreen) * f + STRAW_COLOR1.getGreen).toInt
val b: Int = ((STRAW_COLOR2.getBlue - STRAW_COLOR1.getBlue) * f + STRAW_COLOR1.getBlue).toInt
return new Color(
math.min(255, math.max(0, r)),
math.min(255, math.max(0, g)),
math.min(255, math.max(0, b))
)
}
}
object NaturalTree {
def main(args: Array[String]): Unit = {
val tree: NaturalTree = new NaturalTree(600, 600, 25)
tree.jump(300, 600)
tree.setAngle(-90)
tree.drawTree(8, 80)
for (i: Int <- 0 until 10) {
tree.jump(Random.between(0, 600), 600)
tree.setAngle(-90)
tree.drawTree(3, 20)
}
}
}

47
src/logo/Snowflake.scala Normal file
View File

@@ -0,0 +1,47 @@
package logo
import hevs.graphics.TurtleGraphics
import java.awt.Point
class Snowflake(width: Int, height: Int) extends TurtleGraphics(width, height) {
def drawFlakeSegment(n: Int, length: Double): Unit = {
if (n == 1) {
forward(length)
return
}
val third: Double = length / 3
drawFlakeSegment(n - 1, third)
turn(-60)
drawFlakeSegment(n - 1, third)
turn(120)
drawFlakeSegment(n - 1, third)
turn(-60)
drawFlakeSegment(n - 1, third)
}
def drawFlake(n: Int, length: Double): Unit = {
val pos: Point = getPosition()
val x: Double = pos.x - length / 2
val y: Double = pos.y - math.sqrt(3) * length / 6
jump(x.toInt, y.toInt)
setAngle(0)
drawFlakeSegment(n, length)
turn(120)
drawFlakeSegment(n, length)
turn(120)
drawFlakeSegment(n, length)
turn(120)
}
}
object Snowflake {
def main(args: Array[String]): Unit = {
val win = new Snowflake(600, 600)
win.penUp()
win.jump(300, 300)
win.penDown()
win.drawFlake(5, 400)
}
}

38
src/logo/TestLogo.scala Normal file
View File

@@ -0,0 +1,38 @@
package logo
import hevs.graphics.TurtleGraphics
import java.awt.Color
object TestLogo {
def drawCircle(turtle: TurtleGraphics, radius: Double, resolution: Int = 24): Unit = {
val angle: Double = 360.0 / resolution
val side: Double = radius * math.sin(angle * math.Pi / 180)
turtle.forward(-side / 2)
for (_: Int <- 0 until resolution) {
turtle.forward(side)
turtle.turn(angle)
}
}
def main(args: Array[String]): Unit = {
val turtle: TurtleGraphics = new TurtleGraphics(600, 600)
turtle.drawLine(150, 300, 450, 300)
turtle.penUp()
turtle.jump(300, 300)
turtle.setAngle(0)
turtle.forward(150 * math.sqrt(3) / 2)
turtle.turn(150)
turtle.penDown()
for (_: Int <- 0 until 3) {
turtle.forward(300)
turtle.turn(120)
}
turtle.jump(450, 300)
turtle.setAngle(90)
turtle.setColor(Color.RED)
drawCircle(turtle, 150)
}
}

41
src/logo/Tree.scala Normal file
View File

@@ -0,0 +1,41 @@
package logo
import hevs.graphics.TurtleGraphics
import java.awt.Point
class Tree(width: Int, height: Int, var angleStep: Double) extends TurtleGraphics(width, height) {
def drawTree(n: Int, length: Double): Unit = {
if (n > 1) {
drawBranch(length)
val pos: Point = getPosition()
val angle: Double = getTurtleAngle()
turn(-angleStep)
drawTree(n - 1, length * 0.8)
jump(pos.x, pos.y)
setAngle(angle)
turn(angleStep)
drawTree(n - 1, length * 0.8)
} else {
drawBranch(length)
drawLeaf(length / 10)
}
}
def drawBranch(length: Double): Unit = {
forward(length)
}
def drawLeaf(length: Double): Unit = {
}
}
object Tree {
def main(args: Array[String]): Unit = {
val tree: Tree = new Tree(400, 400, 15)
tree.jump(200, 400)
tree.setAngle(-90)
tree.drawTree(8, 60)
}
}

View File

@@ -0,0 +1,5 @@
package magic_squares
trait Displayable {
def display(g: Grid): Unit
}

View File

@@ -0,0 +1,53 @@
package magic_squares
import hevs.graphics.FunGraphics
import java.awt.{Color, Font}
import javax.swing.SwingConstants
trait GraphicsDisplay extends Displayable {
private var _win: Option[FunGraphics] = None
private val WIDTH: Int = 400
private val HEIGHT: Int = 400
private val MARGIN: Int = 20
private val FONT: Font = new Font("Arial", Font.PLAIN, 24)
override def display(g: Grid): Unit = {
if (_win.isEmpty) {
_win = Some(new FunGraphics(WIDTH, HEIGHT))
}
val win: FunGraphics = _win.get
win.clear()
val size: Int = g.length
val w: Int = WIDTH - 2 * MARGIN
val h: Int = HEIGHT - 2 * MARGIN
val gridSize: Int = math.min(w, h)
val cellSize: Int = gridSize / size
val ox: Int = (WIDTH - gridSize) / 2
val oy: Int = (HEIGHT - gridSize) / 2
win.drawRect(ox, oy, gridSize, gridSize)
for (i: Int <- 1 until size) {
win.drawLine(ox + cellSize * i, oy, ox + cellSize * i, oy + gridSize)
win.drawLine(ox, oy + cellSize * i, ox + gridSize, oy + cellSize * i)
}
for (y: Int <- 0 until size) {
for (x: Int <- 0 until size) {
val v: Int = g(y)(x)
if (v != 0) {
win.drawString(
(ox + cellSize * (x + 0.5)).toInt,
(oy + cellSize * (y + 0.5)).toInt,
v.toString,
FONT,
Color.BLACK,
SwingConstants.CENTER,
SwingConstants.CENTER
)
}
}
}
win.syncGameLogic(60)
}
}

View File

@@ -0,0 +1,156 @@
package magic_squares
abstract class MagicSquareSolver(initialGrid: Grid) extends Displayable {
protected var _initial: Grid = initialGrid
protected val _size: Int = _initial.length
protected val SUM: Int = _size * (_size * _size + 1) / 2
protected val DEBUG: Boolean = false
protected def print(grid: Grid): Unit = {
for (y: Int <- 0 until _size) {
println(grid(y).mkString(","))
}
}
protected def copy(grid: Grid): Grid = {
return grid.map(_.clone())
}
def solve(): Unit = {
val sol: Option[Grid] = solveFrom(_initial, 0, 0)
if (sol.isEmpty) {
println("No solution")
} else {
print(sol.get)
}
}
private def solveFrom(grid: Grid, x: Int, y: Int): Option[Grid] = {
if (DEBUG) println(s"Solving from $x, $y")
if (DEBUG) print(grid)
display(grid)
if (!isValid(grid)) {
if (DEBUG) println(" Grid is invalid")
return None
}
if (y >= _size) {
if (DEBUG) println(" Found solution")
return Some(grid)
}
var values: Array[Int] = Array(_initial(y)(x))
if (values(0) == 0) values = (1 to _size * _size).toArray
if (DEBUG) println(s" Values to test: " + values.mkString("[", ", ", "]"))
val newGrid: Grid = copy(grid)
var x2: Int = x + 1
var y2: Int = y
if (x2 >= _size) {
x2 -= _size
y2 += 1
}
for (i: Int <- values) {
if (DEBUG) println(s" Testing $i")
newGrid(y)(x) = i
val sol: Option[Grid] = solveFrom(newGrid, x2, y2)
if (sol.isDefined) {
if (DEBUG) println(" Found solution, collapsing call stack")
return sol
}
}
if (DEBUG) println(s" No solution for this configuration")
return None
}
private def isValid(grid: Grid): Boolean = {
val values: Array[Int] = grid.reduce((a, b) => a.concat(b)).filter(_ != 0)
if (values.distinct.length != values.length) return false
val diag1: Array[Int] = new Array(_size)
val diag2: Array[Int] = new Array(_size)
for (i: Int <- 0 until _size) {
val row: Array[Int] = grid(i)
val col: Array[Int] = grid.map(_(i))
diag1(i) = row(i)
diag2(i) = col(_size - i - 1)
if (!isLineValid(row)) {
if (DEBUG) println(s" -> row $i is invalid: " + row.mkString("[", ", ", "]"))
return false
}
if (!isLineValid(col)) {
if (DEBUG) println(s" -> column $i is invalid: " + col.mkString("[", ", ", "]"))
return false
}
}
if (!isLineValid(diag1)) {
if (DEBUG) println(s" -> diag1 is invalid: " + diag1.mkString("[", ", ", "]"))
return false
}
if (!isLineValid(diag2)) {
if (DEBUG) println(s" -> diag2 is invalid: " + diag2.mkString("[", ", ", "]"))
return false
}
return true
}
private def isLineValid(line: Array[Int]): Boolean = {
val sum: Int = line.sum
if (line.contains(0)) {
if (sum > SUM) return false
} else if (sum != SUM) return false
return true
}
}
object MagicSquareSolver {
def main(args: Array[String]): Unit = {
val solver1: MagicSquareSolver = new MagicSquareSolver(Array(
Array(8, 0, 0),
Array(0, 0, 7),
Array(0, 9, 0)
)) with TextDisplay
solver1.solve()
println()
/*
8,1,6
3,5,7
4,9,2
*/
val solver2: MagicSquareSolver = new MagicSquareSolver(Array(
Array(9, 0, 0),
Array(0, 0, 7),
Array(0, 8, 0)
)) with TextDisplay
solver2.solve()
println()
/*
no solution
*/
val solver3: MagicSquareSolver = new MagicSquareSolver(Array(
Array(0, 0, 2),
Array(0, 5, 7),
Array(0, 0, 0)
)) with GraphicsDisplay
solver3.solve()
/*
4,9,2
3,5,7
8,1,6
*/
val solver4: MagicSquareSolver = new MagicSquareSolver(Array(
Array(11, 0, 0, 20, 3),
Array(4, 12, 0, 0, 16),
Array(0, 5, 13, 21, 9),
Array(10, 0, 0, 14, 22),
Array(23, 6, 19, 2, 0),
)) with GraphicsDisplay
solver4.solve()
}
}

View File

@@ -0,0 +1,9 @@
package magic_squares
trait TextDisplay extends Displayable {
override def display(g: Grid): Unit = {
for (y: Int <- g.indices) {
println(g(y).mkString(","))
}
}
}

View File

@@ -0,0 +1,3 @@
package object magic_squares {
type Grid = Array[Array[Int]]
}