munit-scalacheck β ΡΡΠΎ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° ΠΈΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΠΈ, ΠΊΠΎΡΠΎΡΠ°Ρ ΠΎΠ±ΡΠ΅Π΄ΠΈΠ½ΡΠ΅Ρ Π΄Π²Π° ΠΌΠΎΡΠ½ΡΡ
ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΠ° ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ Π² ΡΠΊΠΎΡΠΈΡΡΠ΅ΠΌΠ΅ Scala:
- MUnit β ΡΠΎΠ²ΡΠ΅ΠΌΠ΅Π½Π½ΡΠΉ, ΡΠ²Π΅ΡΡ Π±ΡΡΡΡΡΠΉ ΠΈ Π»Π΅Π³ΠΊΠΎΠ²Π΅ΡΠ½ΡΠΉ ΡΡΠ΅ΠΉΠΌΠ²ΠΎΡΠΊ Π΄Π»Ρ ΡΠ½ΠΈΡ-ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ, ΡΠΎΠ·Π΄Π°Π½Π½ΡΠΉ ΡΠΏΠ΅ΡΠΈΠ°Π»ΡΠ½ΠΎ ΠΏΠΎΠ΄ Scala 3 (Ρ Π½Π°ΡΠΈΠ²Π½ΠΎΠΉ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠΎΠΉ ΠΊΠ°ΠΊ JVM, ΡΠ°ΠΊ ΠΈ Scala.js).
- ScalaCheck β Π·ΠΎΠ»ΠΎΡΠΎΠΉ ΡΡΠ°Π½Π΄Π°ΡΡ Π΄Π»Ρ Property-Based Testing (ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ ΡΠ²ΠΎΠΉΡΡΠ²) Π² Scala (Π²Π΄ΠΎΡ Π½ΠΎΠ²Π»Π΅Π½Π½ΡΠΉ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΎΠΉ QuickCheck ΠΈΠ· Haskell).
ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° munit-scalacheck ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ ΠΏΠΈΡΠ°ΡΡ ΡΠ΅ΡΡΡ ScalaCheck ΠΏΡΡΠΌΠΎ Π²Π½ΡΡΡΠΈ ΠΏΡΠΈΠ²ΡΡΠ½ΡΡ
ΡΠ΅ΡΡ-ΠΊΠ»Π°ΡΡΠΎΠ² MUnit, ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡ ΡΠ΄ΠΎΠ±Π½ΠΎΠ΅ ΠΊΠ»ΡΡΠ΅Π²ΠΎΠ΅ ΡΠ»ΠΎΠ²ΠΎ property(...) Π²ΠΌΠ΅ΡΡΠΎ ΡΡΠ°Π½Π΄Π°ΡΡΠ½ΠΎΠ³ΠΎ test(...).
Π§ΡΠΎ ΡΠ°ΠΊΠΎΠ΅ Property-Based Testing (Π’Π΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ ΡΠ²ΠΎΠΉΡΡΠ²)?
Π ΠΎΠ±ΡΡΠ½ΠΎΠΌ ΡΠ½ΠΈΡ-ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠΈ ΠΌΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π½Π° ΠΏΡΠΈΠΌΠ΅ΡΠ°Ρ (Example-Based Testing):
- ΠΡ Π²ΡΡΡΠ½ΡΡ ΠΏΠΈΡΠ΅ΠΌ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΡΠ΅ Π²Ρ ΠΎΠ΄Π½ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅: Β«ΠΠ°Π½Π° Π½Π°ΡΠ°Π»ΡΠ½Π°Ρ ΠΏΠΎΠ·ΠΈΡΠΈΡ ΠΈ ΠΊΡΠ±ΠΈΠΊΠΈ [1, 2].Β»
- ΠΡ Π²ΡΡΡΠ½ΡΡ ΠΏΠΈΡΠ΅ΠΌ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΡΠΉ ΠΎΠΆΠΈΠ΄Π°Π΅ΠΌΡΠΉ ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ: Β«ΠΠΎΠ»ΠΆΠ½ΠΎ Π±ΡΡΡ ΡΠΎΠ²Π½ΠΎ 20 Π»Π΅Π³Π°Π»ΡΠ½ΡΡ Ρ ΠΎΠ΄ΠΎΠ².Β»
Π Property-Based Testing ΠΌΡ Π½Π΅ ΠΏΡΠΈΠ΄ΡΠΌΡΠ²Π°Π΅ΠΌ ΠΏΡΠΈΠΌΠ΅ΡΡ ΡΠ°ΠΌΠΈ. ΠΠΌΠ΅ΡΡΠΎ ΡΡΠΎΠ³ΠΎ ΠΌΡ ΠΎΠΏΠΈΡΡΠ²Π°Π΅ΠΌ ΠΈΠ½Π²Π°ΡΠΈΠ°Π½ΡΡ (ΡΠ²ΠΎΠΉΡΡΠ²Π°) β ΠΏΡΠ°Π²ΠΈΠ»Π°, ΠΊΠΎΡΠΎΡΡΠ΅ Π΄ΠΎΠ»ΠΆΠ½Ρ ΠΎΡΡΠ°Π²Π°ΡΡΡΡ ΠΈΡΡΠΈΠ½Π½ΡΠΌΠΈ Π΄Π»Ρ Π»ΡΠ±ΡΡ Π²Ρ ΠΎΠ΄Π½ΡΡ Π΄Π°Π½Π½ΡΡ .
ΠΡΠΈΠΌΠ΅Ρ ΡΠ²ΠΎΠΉΡΡΠ²Π° Π΄Π»Ρ Dice Chess:
Β«ΠΠ»Ρ Π»ΡΠ±ΠΎΠΉ Π²Π°Π»ΠΈΠ΄Π½ΠΎΠΉ ΡΠ°Ρ ΠΌΠ°ΡΠ½ΠΎΠΉ ΠΏΠΎΠ·ΠΈΡΠΈΠΈ ΠΈ Π΄Π»Ρ Π»ΡΠ±ΠΎΠ³ΠΎ ΡΠ»ΡΡΠ°ΠΉΠ½ΠΎΠ³ΠΎ Π±ΡΠΎΡΠΊΠ° 3 ΠΊΡΠ±ΠΈΠΊΠΎΠ² , Π΅ΡΠ»ΠΈ ΠΌΡ ΠΎΡΡΠΈΠ»ΡΡΡΡΠ΅ΠΌ Ρ ΠΎΠ΄Ρ, ΡΠΎ ΠΊΠ°ΠΆΠ΄ΡΠΉ Π²ΠΎΠ·Π²ΡΠ°ΡΠ΅Π½Π½ΡΠΉ Ρ ΠΎΠ΄ ΠΎΠ±ΡΠ·Π°Π½ Π»ΠΈΠ±ΠΎ Π²Π΅ΡΡΠΈ ΠΊ Π²Π·ΡΡΠΈΡ ΠΊΠΎΡΠΎΠ»Ρ, Π»ΠΈΠ±ΠΎ Π±ΡΡΡ ΡΠ°ΡΡΡΡ ΡΠ΅ΠΏΠΎΡΠΊΠΈ Ρ ΠΎΠ΄ΠΎΠ² ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΠΎΠΉ Π΄Π»ΠΈΠ½Ρ.Β»
ScalaCheck Π±Π΅ΡΠ΅Ρ ΡΡΠΎ ΡΠ²ΠΎΠΉΡΡΠ²ΠΎ ΠΈ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ Π³Π΅Π½Π΅ΡΠΈΡΡΠ΅Ρ 100 (ΠΈΠ»ΠΈ 1000) Π°Π±ΡΠΎΠ»ΡΡΠ½ΠΎ ΡΠ»ΡΡΠ°ΠΉΠ½ΡΡ ΠΊΠΎΠΌΠ±ΠΈΠ½Π°ΡΠΈΠΉ ΠΏΠΎΠ·ΠΈΡΠΈΠΉ ΠΈ ΠΊΡΠ±ΠΈΠΊΠΎΠ², ΠΏΡΠΎΠ²Π΅ΡΡΡ, Π½Π΅ ΡΠ»ΠΎΠΌΠ°Π΅ΡΡΡ Π»ΠΈ Π½Π°Ρ ΠΊΠΎΠ΄ Π½Π° ΠΊΠ°ΠΊΠΎΠΌ-Π½ΠΈΠ±ΡΠ΄Ρ Π±Π΅Π·ΡΠΌΠ½ΠΎΠΌ edge-case.
ΠΠ»Ρ ΡΠ΅Π³ΠΎ munit-scalacheck Π½ΡΠΆΠ΅Π½ Π½Π°ΡΠ΅ΠΌΡ ΡΠ°Ρ
ΠΌΠ°ΡΠ½ΠΎΠΌΡ Π΄Π²ΠΈΠΆΠΊΡ?
ΠΠ»Ρ Π΄Π²ΠΈΠΆΠΊΠ° Dice Chess ΡΠ°ΠΊΠΎΠ΅ ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ β ΡΡΠΎ ΡΠ»ΡΡΠΈΠΌΠ°ΡΠΈΠ²Π½ΠΎΠ΅ ΠΎΡΡΠΆΠΈΠ΅ ΠΏΡΠΎΡΠΈΠ² Π±Π°Π³ΠΎΠ². Π Π²ΠΎΡ ΠΏΠΎΡΠ΅ΠΌΡ:
1. ΠΠΎΡΡΠ±Π° Ρ ΠΊΠΎΠΌΠ±ΠΈΠ½Π°ΡΠΎΡΠ½ΡΠΌ Π²Π·ΡΡΠ²ΠΎΠΌ π₯
Π¨Π°Ρ ΠΌΠ°ΡΡ ΠΈΠΌΠ΅ΡΡ ΠΊΠΎΠ»ΠΎΡΡΠ°Π»ΡΠ½ΠΎΠ΅ ΠΏΡΠΎΡΡΡΠ°Π½ΡΡΠ²ΠΎ ΡΠΎΡΡΠΎΡΠ½ΠΈΠΉ ( ΠΏΠΎΠ·ΠΈΡΠΈΠΉ). ΠΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΡΠ»ΡΡΠ°ΠΉΠ½ΡΡ ΠΊΡΠ±ΠΈΠΊΠΎΠ² ΠΈ ΠΌΠΈΠΊΡΠΎΡ ΠΎΠ΄ΠΎΠ² ΡΠ²Π΅Π»ΠΈΡΠΈΠ²Π°Π΅Ρ ΡΠΈΡΠ»ΠΎ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΡΡ ΡΡΠ΅Π½Π°ΡΠΈΠ΅Π² Π² ΡΠ°Π·Ρ. ΠΠ°ΠΏΠΈΡΠ°ΡΡ ΡΡΡΠ½ΡΠ΅ ΡΠ΅ΡΡΡ Π½Π° Π²ΡΠ΅ ΡΠ»ΡΡΠ°ΠΈ Π±Π»ΠΎΠΊΠΈΡΠΎΠ²ΠΎΠΊ, ΡΠ°Ρ ΠΎΠ², ΡΠΎΠΊΠΈΡΠΎΠ²ΠΎΠΊ ΠΈ ΡΠ»ΠΎΠΆΠ½ΡΡ ΠΏΡΠ΅Π²ΡΠ°ΡΠ΅Π½ΠΈΠΉ ΠΏΠ΅ΡΠ΅ΠΊ ΡΠΈΠ·ΠΈΡΠ΅ΡΠΊΠΈ Π½Π΅Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ. ScalaCheck Π½Π°Ρ ΠΎΠ΄ΠΈΡ ΡΡΠ΅Π½Π°ΡΠΈΠΈ, ΠΎ ΠΊΠΎΡΠΎΡΡΡ ΡΠ΅Π»ΠΎΠ²Π΅ΠΊ-ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊ Π΄Π°ΠΆΠ΅ Π½Π΅ Π·Π°Π΄ΡΠΌΠ°Π΅ΡΡΡ (Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ: Β«Π§Π΅ΡΠ½ΡΠΉ ΡΠ»ΠΎΠ½ Π·Π°Π±Π»ΠΎΠΊΠΈΡΠΎΠ²Π°Π½, Π±Π΅Π»ΡΠΉ ΠΊΠΎΡΠΎΠ»Ρ ΠΏΠΎΠ΄ ΡΠ°Ρ ΠΎΠΌ Π½Π° ΠΊΡΠ°Ρ Π΄ΠΎΡΠΊΠΈ, Π° Π½Π° ΠΊΡΠ±ΠΈΠΊΠ°Ρ Π²ΡΠΏΠ°Π»ΠΈ ΡΠΎΠΊΠΈΡΠΎΠ²ΠΊΠ° ΠΈ ΠΏΠ΅ΡΠΊΠ°Β»).
2. ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠΉ ΠΏΠΎΠΈΡΠΊ ΠΊΠΎΠ½ΡΡΠΏΡΠΈΠΌΠ΅ΡΠΎΠ² (Fuzzing) π
ΠΡΠ»ΠΈ Π°Π»Π³ΠΎΡΠΈΡΠΌ ΡΠΈΠ»ΡΡΡΠ°ΡΠΈΠΈ Ρ ΠΎΠ΄ΠΎΠ² ΡΠΎΠ΄Π΅ΡΠΆΠΈΡ Π»ΠΎΠ³ΠΈΡΠ΅ΡΠΊΡΡ ΠΎΡΠΈΠ±ΠΊΡ, ScalaCheck Π±ΡΡΡΡΠΎ ΡΠ³Π΅Π½Π΅ΡΠΈΡΡΠ΅Ρ ΡΠ»ΡΡΠ°ΠΉΠ½ΡΡ ΠΏΠΎΠ·ΠΈΡΠΈΡ, Π½Π° ΠΊΠΎΡΠΎΡΠΎΠΉ ΡΠ²ΠΎΠΉΡΡΠ²ΠΎ Π½Π΅ Π²ΡΠΏΠΎΠ»Π½ΠΈΡΡΡ, ΠΈ Π²ΡΠ΄Π°ΡΡ Π΅Ρ Π½Π°ΠΌ.
3. Π£ΠΌΠ½ΠΎΠ΅ ΡΠΆΠ°ΡΠΈΠ΅ ΠΎΡΠΈΠ±ΠΎΠΊ (Shrinking) π
ΠΡΠΎ ΠΎΠ΄Π½Π° ΠΈΠ· ΡΠ°ΠΌΡΡ
ΠΊΡΡΡΡΡ
ΡΠΈΡ ScalaCheck. ΠΡΠ»ΠΈ ΡΡΠ΅ΠΉΠΌΠ²ΠΎΡΠΊ Π½Π°ΡΠ΅Π» Π±Π°Π³ Π½Π° ΡΠ»ΠΎΠΆΠ½Π΅ΠΉΡΠ΅ΠΉ ΠΏΠΎΠ·ΠΈΡΠΈΠΈ Ρ 32 ΡΠΈΠ³ΡΡΠ°ΠΌΠΈ ΠΈ ΠΊΡΠ±ΠΈΠΊΠ°ΠΌΠΈ [1, 4, 6], ΠΎΠ½ Π½Π΅ ΠΏΡΠΎΡΡΠΎ Π²ΡΠΏΠ»ΡΠ½Π΅Ρ ΡΡΡ ΠΏΠΎΠ·ΠΈΡΠΈΡ. ΠΠ½ Π½Π°ΡΠ½Π΅Ρ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ Π΅Ρ ΡΠΏΡΠΎΡΠ°ΡΡ: ΡΠ±ΠΈΡΠ°ΡΡ Π»ΠΈΡΠ½ΠΈΠ΅ ΡΠΈΠ³ΡΡΡ Ρ Π΄ΠΎΡΠΊΠΈ, ΠΌΠ΅Π½ΡΡΡ ΠΊΡΠ±ΠΈΠΊΠΈ Π½Π° Π±ΠΎΠ»Π΅Π΅ ΠΏΡΠΎΡΡΡΠ΅, ΠΏΠΎΠΊΠ° Π½Π΅ Π½Π°ΠΉΠ΄Π΅Ρ ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡΠ½ΠΎ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΡΡ ΠΏΠΎΠ·ΠΈΡΠΈΡ, Π½Π° ΠΊΠΎΡΠΎΡΠΎΠΉ Π±Π°Π³ Π²ΡΡ Π΅ΡΠ΅ Π²ΠΎΡΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΡΡ. ΠΡΠΎ Π΄Π΅Π»Π°Π΅Ρ ΠΎΡΠ»Π°Π΄ΠΊΡ Π½Π΅Π²Π΅ΡΠΎΡΡΠ½ΠΎ ΠΏΡΠΎΡΡΠΎΠΉ!
ΠΠ°ΠΊ ΡΡΠΎ Π²ΡΠ³Π»ΡΠ΄ΠΈΡ Π² ΠΊΠΎΠ΄Π΅?
ΠΠΎΡ ΠΏΡΠΎΡΡΠΎΠΉ ΠΌΠ°ΡΠ΅ΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠΉ ΠΏΡΠΈΠΌΠ΅Ρ:
import munit.ScalaCheckSuite
import org.scalacheck.Prop._
class MathSpec extends ScalaCheckSuite {
// ΠΠΌΠ΅ΡΡΠΎ test() ΠΏΠΈΡΠ΅ΠΌ property()
property("ΡΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΊΠΎΠΌΠΌΡΡΠ°ΡΠΈΠ²Π½ΠΎ (a + b == b + a)") {
// forAll Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ ΡΠ³Π΅Π½Π΅ΡΠΈΡΡΠ΅Ρ ΡΠΎΡΠ½ΠΈ ΡΠ»ΡΡΠ°ΠΉΠ½ΡΡ
ΡΠΈΡΠ΅Π» a ΠΈ b
forAll { (a: Int, b: Int) =>
a + b == b + a
}
}
}Π Π²ΠΎΡ ΠΏΡΠΈΠΌΠ΅Ρ ΠΈΠ· Π½Π°ΡΠ΅Π³ΠΎ ΡΠΎΠ·Π΄Π°Π½Π½ΠΎΠ³ΠΎ ΡΠ°ΠΉΠ»Π° MutableLegalMovesFilterSpec.scala (Area D):
// ΠΡ ΡΠΎΠ·Π΄Π°Π»ΠΈ Π³Π΅Π½Π΅ΡΠ°ΡΠΎΡ ΡΠ»ΡΡΠ°ΠΉΠ½ΡΡ
Π±ΡΠΎΡΠΊΠΎΠ² (3 ΡΠΈΡΠ»Π° ΠΎΡ 1 Π΄ΠΎ 6)
val diceGen: Gen[List[Int]] =
Gen.listOfN(3, Gen.choose(1, 6))
property("D3: ΠΡΠ΅ ΠΎΡΡΠΈΠ»ΡΡΡΠΎΠ²Π°Π½Π½ΡΠ΅ Ρ
ΠΎΠ΄Ρ ΡΠ²Π»ΡΡΡΡΡ ΠΏΠΎΠ΄ΠΌΠ½ΠΎΠΆΠ΅ΡΡΠ²ΠΎΠΌ ΠΏΡΠ΅Π²Π΄ΠΎΠ»Π΅Π³Π°Π»ΡΠ½ΡΡ
") {
// forAll Π±Π΅ΡΠ΅Ρ ΡΠ»ΡΡΠ°ΠΉΠ½ΡΡ ΠΏΠΎΠ·ΠΈΡΠΈΡ ΠΈΠ· Π±Π°Π·Ρ ΠΈ ΡΠ»ΡΡΠ°ΠΉΠ½ΡΠΉ Π±ΡΠΎΡΠΎΠΊ
forAll(gameStateGen, diceGen) { (state, dice) =>
val legal = filterMoves(state, dice)
val allPseudo = dice.distinct.flatMap(d => MoveGenerator.generateMoves(state, d))
// Π‘Π²ΠΎΠΉΡΡΠ²ΠΎ: ΠΎΡΡΠΈΠ»ΡΡΡΠΎΠ²Π°Π½Π½ΡΠΉ ΡΠΏΠΈΡΠΎΠΊ Ρ
ΠΎΠ΄ΠΎΠ² Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΡ
// Ρ
ΠΎΠ΄, ΠΊΠΎΡΠΎΡΡΠΉ Π²ΠΎΠΎΠ±ΡΠ΅ Π½Π΅ Π³Π΅Π½Π΅ΡΠΈΡΠΎΠ²Π°Π»ΡΡ ΠΊΡΠ±ΠΈΠΊΠ°ΠΌΠΈ!
legal.forall(allPseudo.contains)
}
}Π Π΅Π·ΡΠΌΠ΅:
munit-scalacheck β ΡΡΠΎ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½Ρ, ΠΊΠΎΡΠΎΡΡΠΉ ΠΏΡΠ΅Π²ΡΠ°ΡΠ°Π΅Ρ ΡΠΊΡΡΠ½ΠΎΠ΅ Π½Π°ΠΏΠΈΡΠ°Π½ΠΈΠ΅ ΡΠΎΡΠ΅Π½ ΠΎΠ΄Π½ΠΎΡΠΈΠΏΠ½ΡΡ
ΡΠ½ΠΈΡ-ΡΠ΅ΡΡΠΎΠ² Π² ΠΏΡΠΎΠ΅ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ ΡΡΡΠΎΠ³ΠΈΡ
ΠΌΠ°ΡΠ΅ΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΡ
ΡΠ²ΠΎΠΉΡΡΠ². ΠΠ½ Π³Π°ΡΠ°Π½ΡΠΈΡΡΠ΅Ρ Π²ΡΡΠΎΡΠ°ΠΉΡΡΡ Π½Π°Π΄Π΅ΠΆΠ½ΠΎΡΡΡ ΠΈ ΡΡΠ°Π±ΠΈΠ»ΡΠ½ΠΎΡΡΡ Π΄Π²ΠΈΠΆΠΊΠ° Dice Chess ΠΏΡΠΈ Π»ΡΠ±ΡΡ
ΡΠ»ΡΡΠ°ΠΉΠ½ΡΡ
ΠΈΠ³ΡΠΎΠ²ΡΡ
ΡΠΈΡΡΠ°ΡΠΈΡΡ
!