Skip to content

Instantly share code, notes, and snippets.

@anikkon
Last active December 25, 2017 23:06
Show Gist options
  • Save anikkon/45e335e2fbcbcc79004a84b8a2d7d11e to your computer and use it in GitHub Desktop.
Save anikkon/45e335e2fbcbcc79004a84b8a2d7d11e to your computer and use it in GitHub Desktop.
BATTLESHIP SIMULATION
val random = java.util.Random()
data class TorpedoAttack(val source: Int, val target: Int)
// CommandCenter interface with the default behaviour
interface CommandCenter {
fun fireTorpedo(ships: BooleanArray): TorpedoAttack {
// Flagship is marked as Boolean TRUE
val flagship = ships.indexOfFirst { it }
// Fire torpedo from flagship at random target
return TorpedoAttack(flagship, random.nextInt(5))
}
fun guideTorpedo(attack: TorpedoAttack): Int {
// Return either -1, 0, 1 to move torpedo a bit
return random.nextInt(3) - 1
}
fun onTorpedoDetected(attack: TorpedoAttack) {
// Triggered when the opponent fires his torpedo
}
}
fun simulate(): Pair<Boolean, Boolean> {
// Initialize command centers
val yourCC = YourCommandCenter()
val enemyCC = EnemyCommandCenter()
// Initialize battleships
val yourShips = BooleanArray(5)
val enemyShips = BooleanArray(5)
// Randomly place flagships
yourShips[random.nextInt(yourShips.size)] = true
enemyShips[random.nextInt(yourShips.size)] = true
// Your fire first
val yourAttack = yourCC.fireTorpedo(yourShips)
enemyCC.onTorpedoDetected(yourAttack)
// Enemy fires second
val enemyAttack = enemyCC.fireTorpedo(enemyShips)
yourCC.onTorpedoDetected(enemyAttack)
// You guide your torpedo
val yourTarget = yourAttack.target + yourCC.guideTorpedo(yourAttack) % 2
// Enemy guides his torpedo
val enemyTarget = enemyAttack.target + enemyCC.guideTorpedo(enemyAttack) % 2
// Check if each side's flagship has been hit
// Note: Both players can be winners at the same time
return Pair(
yourShips.getOrElse(enemyTarget) { false },
enemyShips.getOrElse(yourTarget) { false }
)
}
fun main(args: Array<String>) {
var yourWins = 0
var enemyWins = 0
for (i in 1..100000) {
val (enemyWon, youWon) = simulate()
if (youWon) yourWins++
if (enemyWon) enemyWins++
}
val ratio = yourWins.toDouble() / enemyWins.toDouble()
println("Your wins to enemy's wins ratio is $ratio")
if (ratio < 1.0) println("You lost! Improve your battle logic!")
else if (ratio < 1.25) println("You won by a small margin! Can you do better?")
else println("Congratulations! Your victory is undeniable. Good job!")
}
class EnemyCommandCenter : CommandCenter {
var attackedShip = -1
override fun fireTorpedo(ships: BooleanArray): TorpedoAttack {
// Enemy has overridden this method to hide his flagship
val flagship = ships.indexOfFirst { it }
var source: Int
do source = random.nextInt(5) while (source == flagship || source == attackedShip)
return TorpedoAttack(source, random.nextInt(5))
}
override fun guideTorpedo(attack: TorpedoAttack): Int {
// Ensure that torpedo doesn't sway off and always hits at least some ship
return if (attack.target in 1..3) random.nextInt(3) - 1 else 0
}
override fun onTorpedoDetected(attack: TorpedoAttack) {
attackedShip = attack.target
}
}
class YourCommandCenter : CommandCenter {
/** YourCommandCenter takes control over random = java.util.Random(),
* used to generate the position of flagships
* Therefore, it's possible to predict which values will be generated
*
* weak spot > val random = java.util.Random()
* > enemyShips[random.nextInt(yourShips.size)] = true
*/
/*initializes the seed of random once an instance of the class is created
* even before the first call by the enemy */
init {
val tmp = java.util.Random().nextLong()
//converting Long to 48-bit seed
val seed = (tmp shl 48) - 1
//set seed
random.setSeed(seed)
//simulate calls made in simulate()
random.nextInt()
vars.nextRand = random.nextInt(5)
//set the seed again as it has been changed by previous calls
random.setSeed(seed)
}
object vars {
var nextRand: Int? = null
}
override fun fireTorpedo(ships: BooleanArray): TorpedoAttack {
// Flagship is marked as Boolean TRUE
val flagship = ships.indexOfFirst { it }
//Predicted value generated by nextInt()
val target = vars.nextRand!!
return TorpedoAttack(flagship, target)
}
//no need to guide the torpedo - the position of flagship is known
override fun guideTorpedo(attack: TorpedoAttack): Int = 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment