Create a gist now

Instantly share code, notes, and snippets.

Embed
KotlinとJavaFXで遺伝的アルゴリズムで、特定の文字列に進化させる
import javafx.animation.AnimationTimer
import javafx.application.Application
import javafx.scene.Group
import javafx.scene.Scene
import javafx.scene.canvas.Canvas
import javafx.scene.canvas.GraphicsContext
import javafx.scene.paint.Color
import javafx.scene.text.Font
import javafx.stage.Stage
fun random(min: Double = 0.0, max: Double = 1.1) = (Math.random() * (max - min + 1)) + min
fun newChar(): Char {
var c = Math.floor(random(64.0, 122.0)).toInt()
c = if (c == 64) 32 else c // @ mark to space.
return c.toChar()
}
fun main(args: Array<String>) {
Application.launch(App::class.java, *args)
}
/**
* JavaFXのCanvasを用意して、AnimationTimerでゲームループを実行します。
* ProcessingやP5.jsと同じように、アプリ起動時に、start()関数が呼ばれ、
* ゲームループ内で、draw(ctx:GraphicsContext)が呼ばれます。
* start関数とdraw関数はユーザーが実装する必要があります。
*/
class App : Application() {
lateinit var canvas: Canvas
lateinit var ctx: GraphicsContext
override fun start(primaryStage: Stage) {
canvas = Canvas(1280.0, 720.0)
ctx = canvas.graphicsContext2D
val root = Group()
root.children.add(canvas)
object : AnimationTimer() {
override fun handle(now: Long) {
draw(ctx)
}
}.start()
with(primaryStage) {
title = "遺伝的アルゴリズムでランダム文字列を'$TARGET'に進化させる"
scene = Scene(root, canvas.width, canvas.height)
show()
}
}
}
//////////////////////////////////////////////
fun <T> randIndex(src: List<T>): Int = Math.floor(Math.random() * src.size).toInt()
class DNA(targetLength: Int = 10, var fitness: Double = 0.0) {
var genes: List<Char> = List(targetLength, { newChar() })
fun calcFitness(target: String) {
fitness = calcScore() / target.length
}
fun calcScore(): Double = genes.foldIndexed(0.0) { i, acc, c -> if (TARGET[i] == c) acc + 1.0 else acc }
fun crossover(partnerDNA: DNA): DNA {
val childDNA = DNA(genes.size)
val midpoint = Math.floor(Math.random() * genes.size)
childDNA.genes = genes.mapIndexed { i, c -> if (i > midpoint) c else partnerDNA.genes[i] }
return childDNA
}
fun mutate(rate: Double): DNA {
genes = genes.map { if (Math.random() < rate) newChar() else it }
return this
}
}
data class Point(val x: Int, val y: Int)
fun xy(x: Int): Point = Point(x % 4, x / 4)
const val POPULATION_COUNT = 150
const val MUTATION_RATE = 0.01
const val TARGET = "to be or not to be"
var foundFlag = false
// ステップ1. 集団の作成
var population: List<DNA> = List(POPULATION_COUNT) { DNA(TARGET.length) }
var generationCount = 0
fun draw(ctx: GraphicsContext) {
ctx.fill = Color(1.0, 1.0, 1.0, 1.0)
ctx.fillRect(0.0, 0.0, ctx.canvas.width, ctx.canvas.height)
if (!foundFlag) {
// ステップ2.選択
// 集団の各要素の適応度を計算
population.forEach { it.calcFitness(TARGET) }
// 交配プールを作成
val matingPool = population.map { dna ->
val n = Math.floor(dna.fitness * 100.0).toInt()
List(n) { dna }
}.flatten()
// ステップ3.生殖(reproduction)
population = population.map {
val dad = matingPool[randIndex(matingPool)]
val mam = matingPool[randIndex(matingPool)]
val child = dad.crossover(mam)
child.mutate(MUTATION_RATE)
}
population.forEach { it.calcFitness(TARGET) }
foundFlag = (population.indexOfFirst { dna -> dna.genes.joinToString("") == TARGET } != -1)
++generationCount
}
// 表示 /////
ctx.font = Font("Monospaced", 14.0)
population.map { it.genes.joinToString("") }
.forEachIndexed { i, s ->
val (x, y) = xy(i)
ctx.fill = if (s == TARGET) Color.RED else Color.BLACK
ctx.fillText(s, 10.0 + (x * 150), 30.0 + (y * 15))
}
ctx.fill = Color.BLACK
val maxDNA = population.maxBy { dna -> dna.fitness } ?: population[0]
ctx.font = Font("Monospaced", 32.0)
ctx.fillText("Best phrase : ${maxDNA.genes.joinToString("")}", 1280.0 / 2, 50.0)
ctx.font = Font("Monospaced", 20.0)
ctx.fillText("total generations: $generationCount", 1280.0 / 2, 50.0 + 20.0 * 1)
// average
//SUM_DNA_LENGTH
val averageFitness = population.map { it.calcScore() }.average()
ctx.fillText("average fitness : ${Math.floor(averageFitness * 100.0) / 100.0}", 1280.0 / 2, 50.0 + 20.0 * 2)
ctx.fillText("mutation rate : $MUTATION_RATE", 1280.0 / 2, 50.0 + 20.0 * 3)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment