Difference between revisions of "User:Bnorm/CoroutineRobot"
Jump to navigation
Jump to search
(Base robot which uses Kotlin coroutines) |
|||
Line 32: | Line 32: | ||
private class QueueCoroutineDispatcher(threadCount: Int) : CoroutineDispatcher() { | private class QueueCoroutineDispatcher(threadCount: Int) : CoroutineDispatcher() { | ||
− | private val queue = LinkedBlockingQueue<Runnable | + | companion object { |
+ | private val POISON = Runnable {} | ||
+ | } | ||
+ | |||
+ | private val queue = LinkedBlockingQueue<Runnable>() | ||
private val threads = List(threadCount) { | private val threads = List(threadCount) { | ||
thread(name = "Computation $it") { | thread(name = "Computation $it") { | ||
try { | try { | ||
while (true) { | while (true) { | ||
− | val task = queue.take() | + | val task = queue.take() |
+ | if (task === POISON) break | ||
task.run() | task.run() | ||
} | } | ||
Line 53: | Line 58: | ||
repeat(threads.size) { | repeat(threads.size) { | ||
// poison the queue once for each thread | // poison the queue once for each thread | ||
− | queue.put( | + | queue.put(POISON) |
} | } | ||
} | } |
Latest revision as of 05:45, 30 July 2021
CoroutineRobot
Using Kotlin and coroutines, safe and fair multithreading for a Robot can be implemented in Robocode. This can be achieved with structured concurrency and a custom thread dispatcher.
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.runBlocking
import robocode.AdvancedRobot
import java.util.concurrent.LinkedBlockingQueue
import kotlin.concurrent.thread
import kotlin.coroutines.CoroutineContext
abstract class CoroutineRobot : AdvancedRobot() {
private val _computation = QueueCoroutineDispatcher(4)
val Computation: CoroutineDispatcher get() = _computation
private lateinit var _main: CoroutineDispatcher
val Main: CoroutineDispatcher get() = _main
final override fun run() {
try {
runBlocking {
_main = coroutineContext[CoroutineDispatcher]!!
coroutineRun()
}
} finally {
_computation.shutdown()
}
}
abstract suspend fun coroutineRun()
private class QueueCoroutineDispatcher(threadCount: Int) : CoroutineDispatcher() {
companion object {
private val POISON = Runnable {}
}
private val queue = LinkedBlockingQueue<Runnable>()
private val threads = List(threadCount) {
thread(name = "Computation $it") {
try {
while (true) {
val task = queue.take()
if (task === POISON) break
task.run()
}
} catch (ignore: InterruptedException) {
// ignore
}
}
}
override fun dispatch(context: CoroutineContext, block: Runnable) {
queue.put(block)
}
fun shutdown() {
repeat(threads.size) {
// poison the queue once for each thread
queue.put(POISON)
}
}
}
}
Multithreading
With the above base class multithreading can be achieved using coroutineScope, launch, and withContext builder functions.
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class SampleRobot : CoroutineRobot() {
override suspend fun coroutineRun() {
while (true) {
coroutineScope { // Starts a logic container for asynchronous work
launch(Computation) { // Launch asynchronous work on 1 of the 4 computation threads
// Do radar logic
withContext(Main) { // Switch back to run thread
setTurnRadarRightRadians(radarTurn)
}
}
launch(Computation) { // Launch asynchronous work on 1 of the 4 computation threads
// Do firing logic
withContext(Main) { // Switch back to run thread
setTurnGunRightRadians(gunTurn)
setFire(bulletPower)
}
}
launch(Computation) { // Launch asynchronous work on 1 of the 4 computation threads
// Do movement logic
withContext(Main) { // Switch back to run thread
setTurnRightRadians(tankTurn)
setAhead(tankMove)
}
}
}
execute()
}
}
}