Advertisement
Guest User

Untitled

a guest
Nov 21st, 2019
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.74 KB | None | 0 0
  1. import java.time.LocalDateTime
  2. import java.util.*
  3. import kotlin.math.abs
  4. import kotlin.random.Random
  5.  
  6. typealias Tile = Pair<Int, Int>
  7. typealias Board = Array<IntArray>
  8. typealias Path = List<Board>
  9.  
  10. typealias Genome = List<Pair<Board, Tile>>
  11.  
  12. class Puzzle {
  13. companion object {
  14. const val MATRIX_SIZE = 3
  15. val START_POINT = Tile(0, 1)
  16. val END_POINT = Tile(0, 0)
  17. private const val MIN_STATES = 31
  18. const val POPULATION_SIZE = 300
  19. }
  20.  
  21. private inner class TableNode(
  22. val moves: Int,
  23. val path: Path,
  24. val currentState: Board,
  25. val blankTile: Tile
  26. ) : Comparable<TableNode> {
  27. val f: Int
  28. get() = moves + currentState.manhattanDistance()
  29.  
  30. override fun compareTo(other: TableNode): Int {
  31. return this.f.compareTo(other.f)
  32. }
  33. }
  34.  
  35. private val startMatrix: Board = arrayOf(
  36. intArrayOf(8, 0, 6),
  37. intArrayOf(5, 4, 7),
  38. intArrayOf(2, 3, 1)
  39. )
  40.  
  41. private val goalMatrix: Board = arrayOf(
  42. intArrayOf(0, 1, 2),
  43. intArrayOf(3, 4, 5),
  44. intArrayOf(6, 7, 8)
  45. )
  46.  
  47. private fun Board.manhattanDistance(): Int {
  48. var manhattanDistanceSum = 0
  49. for (row in 0 until MATRIX_SIZE)
  50. for (col in 0 until MATRIX_SIZE) {
  51. val value = this[row][col]
  52. if (value != 0) {
  53. val targetX: Int = value / MATRIX_SIZE
  54. val targetY: Int = value % MATRIX_SIZE
  55. val dx = row - targetX
  56. val dy = col - targetY
  57. manhattanDistanceSum += abs(dx) + abs(dy)
  58. }
  59. }
  60. return manhattanDistanceSum
  61. }
  62.  
  63. private fun Board.copy() = Array(size) { get(it).clone() }
  64.  
  65. private fun Board.newState(blankSpace: Tile, newTile: Tile): Board {
  66. val newState = this.copy()
  67. newState[blankSpace.first][blankSpace.second] = newState[newTile.first][newTile.second].also {
  68. newState[newTile.first][newTile.second] = newState[blankSpace.first][blankSpace.second]
  69. }
  70. return newState
  71. }
  72.  
  73. private fun Board.misplacedTiles(): Int {
  74. var result = 0
  75. for (row in 0 until MATRIX_SIZE)
  76. for (col in 0 until MATRIX_SIZE)
  77. if (this[row][col] != goalMatrix[row][col])
  78. result++
  79. return result - 1
  80. }
  81.  
  82. private fun Tile.isInBounds(): Boolean {
  83. return first in 0 until MATRIX_SIZE &&
  84. second in 0 until MATRIX_SIZE
  85. }
  86.  
  87. private val usedTables = mutableListOf<Board>()
  88. private fun MutableList<Board>.isNewState(element: Board): Boolean {
  89. return !this.map { it.contentDeepEquals(element) }.contains(true)
  90. }
  91.  
  92. private fun PriorityQueue<TableNode>.addAndMarkAsPassed(node: TableNode) {
  93. usedTables.add(node.currentState)
  94. add(node)
  95. }
  96.  
  97. fun solveWithAStar(): Pair<Int, Path> {
  98. usedTables.clear()
  99. val priorityQueue = PriorityQueue<TableNode>()
  100. priorityQueue.addAndMarkAsPassed(TableNode(0, listOf<Board>(startMatrix), startMatrix, START_POINT))
  101. while (priorityQueue.isNotEmpty()) {
  102. val currentNode = priorityQueue.peek()
  103. if (currentNode.currentState.manhattanDistance() == 0)
  104. return Pair(currentNode.moves, currentNode.path)
  105. priorityQueue.remove()
  106. Direction.values().forEach { dir ->
  107. val newTile = dir.nextTile(currentNode.blankTile)
  108. if (newTile.isInBounds()) {
  109. val newBoard = currentNode.currentState.newState(currentNode.blankTile, newTile)
  110. if (usedTables.isNewState(newBoard)) {
  111. val path = currentNode.path.toMutableList()
  112. path.add(newBoard)
  113. priorityQueue.addAndMarkAsPassed(TableNode((currentNode.moves + 1), path, newBoard, newTile))
  114. }
  115. }
  116. }
  117. }
  118. return Pair(0, mutableListOf())
  119. }
  120.  
  121. fun solveWithBeamSearch(beamWidth: Int): Pair<Int, Path> {
  122. usedTables.clear()
  123. val priorityQueue = PriorityQueue<TableNode>()
  124. priorityQueue.addAndMarkAsPassed(TableNode(0, listOf<Board>(startMatrix), startMatrix, START_POINT))
  125. while (priorityQueue.isNotEmpty()) {
  126. val currentNode = priorityQueue.peek()
  127. if (currentNode.currentState.manhattanDistance() == 0)
  128. return Pair(currentNode.moves, currentNode.path)
  129. priorityQueue.remove()
  130. val options = PriorityQueue<TableNode>()
  131. Direction.values().forEach { dir ->
  132. val newTile = dir.nextTile(currentNode.blankTile)
  133. if (newTile.isInBounds()) {
  134. val newBoard = currentNode.currentState.newState(currentNode.blankTile, newTile)
  135. if (usedTables.isNewState(newBoard)) {
  136. val path = currentNode.path.toMutableList()
  137. path.add(newBoard)
  138. options.addAndMarkAsPassed(TableNode((currentNode.moves + 1), path, newBoard, newTile))
  139. }
  140. }
  141. }
  142. options.addAll(priorityQueue)
  143. priorityQueue.clear()
  144. repeat(beamWidth) {
  145. if (options.isEmpty()) {
  146. return@repeat
  147. }
  148. priorityQueue.add(options.remove())
  149. }
  150. }
  151. return Pair(0, mutableListOf())
  152. }
  153.  
  154.  
  155. private fun Genome.fitness(): Int {
  156. return this.mapIndexed { index, element -> element.first.manhattanDistance() + index }.sum()
  157. }
  158.  
  159. private fun getSplitIndexes(father: Genome, mother: Genome): Pair<Int, Int>? {
  160. for (index in 1 until father.size)
  161. for (mIndex in 1 until mother.size) {
  162. if (father[index].first.contentDeepEquals(mother[mIndex].first))
  163. return Pair(index, mIndex)
  164. }
  165. return null
  166. }
  167.  
  168. private fun crossOver(father: Genome, mother: Genome): List<Genome> {
  169. val splitIndex = getSplitIndexes(father, mother)
  170. splitIndex?.let {
  171. val fatherFirstHalf = father.slice(0..splitIndex.first)
  172. val fatherSecondHalf = father.drop(splitIndex.first + 1)
  173. val motherFirstHalf = mother.slice(0..splitIndex.second)
  174. val motherSecondHalf = mother.drop(splitIndex.second + 1)
  175. return listOf<Genome>(
  176. fatherFirstHalf.plus(motherSecondHalf),
  177. motherFirstHalf.plus(fatherSecondHalf)
  178. )
  179. }
  180. return mutableListOf()
  181. }
  182.  
  183. private fun Genome.maleMutate(): Genome {
  184. if (4 == Random.nextInt(0, 100)) { // 1%
  185. val newTile = randomDirection(this[lastIndex - 1].second)
  186. if (newTile.isInBounds()) {
  187. val newBoard = last().first.newState(last().second, newTile)
  188. this.mapIndexed { i, _ -> if (i == lastIndex) (Pair(newBoard, newTile)) }
  189. return this
  190. }
  191. }
  192. return this
  193. }
  194.  
  195. private fun Genome.femaleMutate(): Genome {
  196. if (4 == Random.nextInt(0, 100)) { // 1%
  197. val newTile = randomDirection(this[1].second)
  198. if (newTile.isInBounds()) {
  199. val newBoard = first().first.newState(first().second, newTile)
  200. this.mapIndexed { i, _ -> if (i == 0) (Pair(newBoard, newTile)) }
  201. return this
  202. }
  203. }
  204. return this
  205. }
  206.  
  207. private fun generateMalePopulation(): MutableList<Genome> {
  208. val population = mutableListOf<Genome>()
  209. repeat(POPULATION_SIZE / 2) {
  210. population.add(generateGenomeFromBeginning())
  211. }
  212. return population
  213. }
  214.  
  215. private fun generateFemalePopulation(): MutableList<Genome> {
  216. val population = mutableListOf<Genome>()
  217. repeat(POPULATION_SIZE / 2) {
  218. population.add(generateGenomeFromEnd())
  219. }
  220. return population
  221. }
  222.  
  223.  
  224. private fun randomDirection(point: Tile): Tile {
  225. val choices = mutableListOf<Tile>()
  226. Direction.values().forEach {
  227. val newTile: Tile = it.nextTile(point)
  228. if (newTile.isInBounds())
  229. choices.add(newTile)
  230. }
  231. return choices[Random.nextInt(choices.size)]
  232. }
  233.  
  234. private fun generateGenomeFromBeginning(): Genome {
  235. val possibleSolution = mutableListOf(Pair(startMatrix, START_POINT))
  236. val usedPaths = mutableListOf<Board>(startMatrix)
  237. var loop = 0
  238. while (possibleSolution.size < MIN_STATES) {
  239. var flag = true
  240. val currentNode = possibleSolution.last()
  241. while (flag) {
  242. val newTile = randomDirection(currentNode.second)
  243. val newBoard = currentNode.first.newState(currentNode.second, newTile)
  244. if (usedPaths.isNewState(newBoard)) {
  245. usedPaths.add(newBoard)
  246. possibleSolution.add(Pair(newBoard, newTile))
  247. loop = 0
  248. flag = false
  249. }
  250. if (loop > Direction.values().size)
  251. return generateGenomeFromBeginning()
  252. loop++
  253. }
  254. }
  255. return possibleSolution
  256. }
  257.  
  258. private fun generateGenomeFromEnd(): Genome {
  259. val possibleSolution = mutableListOf(Pair(goalMatrix, END_POINT))
  260. val usedPaths = mutableListOf(goalMatrix)
  261. var loop = 0
  262. while (possibleSolution.size < MIN_STATES) {
  263. var flag = true
  264. val currentNode = possibleSolution.first()
  265. while (flag) {
  266. val newTile = randomDirection(currentNode.second)
  267. val newBoard = currentNode.first.newState(currentNode.second, newTile)
  268. if (usedPaths.isNewState(newBoard)) {
  269. usedPaths.add(newBoard)
  270. possibleSolution.add(0, Pair(newBoard, newTile))
  271. loop = 0
  272. flag = false
  273. }
  274. if (loop > Direction.values().size)
  275. return generateGenomeFromEnd()
  276. loop++
  277. }
  278. }
  279. return possibleSolution
  280. }
  281.  
  282. private fun selection(male: Genome, females: List<Genome>): List<Int> {
  283. val connections = mutableListOf<Int>()
  284. females.forEachIndexed { index, female ->
  285. if (getSplitIndexes(male, female) != null)
  286. connections.add(index)
  287. }
  288. return connections
  289. }
  290.  
  291. fun solveWithGeneticAlgorithm(): Genome {
  292. var noBetter = 0
  293. val malePopulation = generateMalePopulation()
  294. val solutions: MutableList<Genome> = mutableListOf()
  295. val femalePopulation = generateFemalePopulation()
  296. var max: Genome = malePopulation.last()
  297. var previousMax: Genome
  298. val newMalePopulation = mutableListOf<Genome>()
  299. val newFemalePopulation = mutableListOf<Genome>()
  300.  
  301. while (!(malePopulation.last().last().first.contentDeepEquals(goalMatrix) && malePopulation.last().size == MIN_STATES)) {
  302. if (noBetter > 10)
  303. return malePopulation.find { genome -> genome.last().first.contentDeepEquals(goalMatrix) }
  304. ?: solutions.minBy { it.size } ?: max
  305. else {
  306. newMalePopulation.clear()
  307. newFemalePopulation.clear()
  308. for (index in 0 until malePopulation.size) {
  309. val connections = selection(malePopulation[index], femalePopulation)
  310. connections.forEach {
  311. val children = crossOver(malePopulation[index], femalePopulation[it])
  312. newMalePopulation.add(children[0])
  313. newFemalePopulation.add(children[1])
  314. solutions.add(children[0])
  315. }
  316. }
  317. malePopulation.asSequence().drop(newMalePopulation.size).plus(newMalePopulation)
  318. .sortedBy { it.fitness() }
  319. .map { it.maleMutate() }.toMutableList()
  320. femalePopulation.asSequence().drop(newFemalePopulation.size).plus(newFemalePopulation)
  321. .sortedBy { it.fitness() }
  322. .map { it.femaleMutate() }.toMutableList()
  323. previousMax = max
  324. solutions.maxBy { it.size }?.let { max = it }
  325. if (previousMax == max)
  326. noBetter++
  327. }
  328. }
  329. return max
  330. }
  331.  
  332. fun printSolution(solution: Pair<Int, Path>) {
  333. println("Number of steps: ${solution.first}")
  334. solution.second.forEach {
  335. println(Arrays.deepToString(it).replace("],", "],\n"))
  336. println("-----------")
  337. // Thread.sleep(2_000)
  338. }
  339. }
  340.  
  341. enum class Direction {
  342. LEFT, RIGHT, UP, DOWN;
  343.  
  344. fun nextTile(point: Tile): Tile {
  345. return when (this) {
  346. LEFT -> Tile(point.first, point.second - 1)
  347. RIGHT -> Tile(point.first, point.second + 1)
  348. UP -> Tile(point.first - 1, point.second)
  349. DOWN -> Tile(point.first + 1, point.second)
  350. }
  351. }
  352. }
  353. }
  354.  
  355. fun main() {
  356. val puzzle = Puzzle()
  357.  
  358. // println("With A* search:")
  359. // puzzle.printSolution(puzzle.solveWithAStar())
  360.  
  361. // println("With Beam search:")
  362. // puzzle.printSolution(puzzle.solveWithBeamSearch(3))
  363.  
  364. println("With GA:")
  365. val solution = puzzle.solveWithGeneticAlgorithm()
  366. puzzle.printSolution(Pair(solution.size - 1, solution.map { it.first }))
  367. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement