Last active
December 25, 2017 23:06
-
-
Save anikkon/45e335e2fbcbcc79004a84b8a2d7d11e to your computer and use it in GitHub Desktop.
BATTLESHIP SIMULATION
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
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