-
-
Save alexruperez/0e56cd5f2a89175a2bc6a6810ecda7d0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package challenge8 | |
import java.io.BufferedReader | |
import java.io.InputStreamReader | |
import java.io.PrintWriter | |
import java.nio.charset.Charset | |
import java.net.ServerSocket | |
import java.net.Socket | |
fun main(args: Array<String>) { | |
echo(TheGameOfLifeTarget(), args.firstOrNull()?.toIntOrNull() ?: 0) | |
} | |
interface Target: Runnable { | |
fun socket(socket: Socket) | |
} | |
fun echo(target: Target, port: Int = 0, backlog: Int = 50) { | |
if (port == 0 || port in 1024..65535) { | |
ServerSocket(port, backlog).use { | |
println("nc localhost ${it.localPort}") | |
val thread = Thread(target) | |
while (!thread.isInterrupted) { | |
target.socket(it.accept()) | |
thread.start() | |
} | |
} | |
} else { | |
throw IllegalArgumentException("The port $port is outside the specified range of valid port values," + | |
"which is 0 (automatically allocated, typically from an ephemeral port range)" + | |
"or between 1024 and 65535, inclusive.") | |
} | |
} | |
open class EchoTarget(private var socket: Socket? = null, | |
private val autoFlush: Boolean = false, | |
private val charset: Charset = Charset.defaultCharset(), | |
private val defaultCharBufferSize: Int = 8192): Target { | |
override fun socket(socket: Socket) { | |
this.socket = socket | |
} | |
override fun run() { | |
val socket = socket ?: throw UninitializedPropertyAccessException("Socket has not been initialized, call fun socket(socket: Socket) first.") | |
val printWriter = PrintWriter(socket.outputStream, autoFlush) | |
val inputStreamReader = InputStreamReader(socket.inputStream, charset) | |
val bufferedReader = BufferedReader(inputStreamReader, defaultCharBufferSize) | |
while (true) { | |
val line = bufferedReader.readLine() | |
handle(line, printWriter) | |
printWriter.flush() | |
if (line == "Close") break | |
} | |
bufferedReader.close() | |
inputStreamReader.close() | |
printWriter.close() | |
socket.close() | |
} | |
open fun handle(line: String, printWriter: PrintWriter) { | |
printWriter.println(line) | |
} | |
} | |
class TheGameOfLifeTarget : EchoTarget() { | |
override fun handle(line: String, printWriter: PrintWriter) { | |
var echo = line | |
val gol = Regex("""^TheGameOfLife\((\d+),(\d+),(\d+)\)$""").find(echo)?.groupValues | |
if (gol != null && gol.size == 4) { | |
val rows = gol[2].toInt() | |
val map = gol[3] | |
if (map.length % rows == 0) { | |
val alive = map.mapIndexed { index, char -> if (char == '1') Cell(index % rows, index / rows) else null }.filterNotNull().toSet() | |
val iteration = gol[1].toInt() | |
echo = TheGameOfLife(alive).play(iteration) | |
} | |
} | |
super.handle(echo, printWriter) | |
} | |
} | |
interface Game { | |
fun play(times: Int): String | |
} | |
data class Cell(private val x: Int, private val y: Int) { | |
fun nextTo() = (-1..1).flatMap { x -> (-1..1).map { y -> Cell(this.x + x, this.y + y) } }.filter { it != this }.toSet() | |
} | |
class TheGameOfLife(private var alive: Set<Cell>): Game { | |
override fun play(times: Int): String { | |
repeat(times) { step() } | |
return alive.size.toString() | |
} | |
private fun cells() = alive.flatMap { it.nextTo() + it }.toSet() | |
private fun aliveNextTo(cell: Cell) = cell.nextTo().intersect(alive).size | |
private fun step() { | |
alive = cells().filter { | |
when (aliveNextTo(it)) { | |
2 -> alive.contains(it) | |
3 -> true | |
else -> false | |
} | |
}.toSet() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment