mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-09 01:54:04 +09:00
working very crude fluid sim
This commit is contained in:
@@ -115,7 +115,9 @@ object Block {
|
|||||||
val SUNSTONE = 257
|
val SUNSTONE = 257
|
||||||
val DAYLIGHT_CAPACITOR = 258
|
val DAYLIGHT_CAPACITOR = 258
|
||||||
|
|
||||||
val FLUID_MARKER = 4095
|
|
||||||
|
val LAVA = 4094
|
||||||
|
val WATER = 4095
|
||||||
|
|
||||||
val NULL = -1
|
val NULL = -1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ object BlockCodex {
|
|||||||
prop.friction = intVal(record, "friction")
|
prop.friction = intVal(record, "friction")
|
||||||
prop.viscosity = intVal(record, "vscs")
|
prop.viscosity = intVal(record, "vscs")
|
||||||
|
|
||||||
prop.isFluid = boolVal(record, "fluid")
|
prop.isFluid = Fluid.isThisTileFluid(prop.id)//boolVal(record, "fluid")
|
||||||
prop.isSolid = boolVal(record, "solid")
|
prop.isSolid = boolVal(record, "solid")
|
||||||
prop.isClear = boolVal(record, "clear")
|
prop.isClear = boolVal(record, "clear")
|
||||||
prop.isWallable = boolVal(record, "wall")
|
prop.isWallable = boolVal(record, "wall")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package net.torvald.terrarum.blockproperties
|
package net.torvald.terrarum.blockproperties
|
||||||
|
|
||||||
import net.torvald.terrarum.gameworld.FluidType
|
import net.torvald.terrarum.gameworld.FluidType
|
||||||
|
import net.torvald.terrarum.gameworld.GameWorld
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by minjaesong on 2016-08-06.
|
* Created by minjaesong on 2016-08-06.
|
||||||
@@ -12,4 +13,8 @@ object Fluid {
|
|||||||
val WATER = FluidType(1)
|
val WATER = FluidType(1)
|
||||||
val STATIC_WATER = FluidType(-1)
|
val STATIC_WATER = FluidType(-1)
|
||||||
|
|
||||||
|
|
||||||
|
fun getFluidTileFrom(type: FluidType) = GameWorld.TILES_SUPPORTED - type.abs()
|
||||||
|
private val fluidTilesRange = 4094..4095
|
||||||
|
fun isThisTileFluid(tileid: Int) = tileid in fluidTilesRange
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
package net.torvald.terrarum.gameworld
|
package net.torvald.terrarum.gameworld
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
|
import net.torvald.terrarum.AppLoader.printdbg
|
||||||
import net.torvald.terrarum.blockproperties.Block
|
import net.torvald.terrarum.blockproperties.Block
|
||||||
import net.torvald.terrarum.realestate.LandUtil
|
import net.torvald.terrarum.realestate.LandUtil
|
||||||
import net.torvald.terrarum.blockproperties.BlockCodex
|
import net.torvald.terrarum.blockproperties.BlockCodex
|
||||||
@@ -215,10 +216,20 @@ open class GameWorld {
|
|||||||
wallDamages.remove(LandUtil.getBlockAddr(this, x, y))
|
wallDamages.remove(LandUtil.getBlockAddr(this, x, y))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warning: this function alters fluid lists: be wary of call order!
|
||||||
|
*/
|
||||||
fun setTileTerrain(x: Int, y: Int, tile: Byte, damage: Int) {
|
fun setTileTerrain(x: Int, y: Int, tile: Byte, damage: Int) {
|
||||||
layerTerrain.setTile(x fmod width, y.coerceWorld(), tile)
|
layerTerrain.setTile(x fmod width, y.coerceWorld(), tile)
|
||||||
layerTerrainLowBits.setData(x fmod width, y.coerceWorld(), damage)
|
layerTerrainLowBits.setData(x fmod width, y.coerceWorld(), damage)
|
||||||
terrainDamages.remove(LandUtil.getBlockAddr(this, x, y))
|
val blockAddr = LandUtil.getBlockAddr(this, x, y)
|
||||||
|
terrainDamages.remove(blockAddr)
|
||||||
|
|
||||||
|
if (!BlockCodex[tile * PairedMapLayer.RANGE + damage].isFluid) {
|
||||||
|
fluidFills.remove(blockAddr)
|
||||||
|
fluidTypes.remove(blockAddr)
|
||||||
|
}
|
||||||
|
// fluid tiles-item should be modified so that they will also place fluid onto their respective map
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTileWire(x: Int, y: Int, tile: Byte) {
|
fun setTileWire(x: Int, y: Int, tile: Byte) {
|
||||||
@@ -343,6 +354,11 @@ open class GameWorld {
|
|||||||
wallDamages[LandUtil.getBlockAddr(this, x, y)] ?: 0f
|
wallDamages[LandUtil.getBlockAddr(this, x, y)] ?: 0f
|
||||||
|
|
||||||
fun setFluid(x: Int, y: Int, fluidType: FluidType, fill: Float) {
|
fun setFluid(x: Int, y: Int, fluidType: FluidType, fill: Float) {
|
||||||
|
/*if (x == 60 && y == 256) {
|
||||||
|
printdbg(this, "Setting fluid $fill at ($x,$y)")
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
val addr = LandUtil.getBlockAddr(this, x, y)
|
val addr = LandUtil.getBlockAddr(this, x, y)
|
||||||
// fluid completely drained
|
// fluid completely drained
|
||||||
if (fill <= WorldSimulator.FLUID_MIN_MASS) {
|
if (fill <= WorldSimulator.FLUID_MIN_MASS) {
|
||||||
@@ -357,10 +373,19 @@ open class GameWorld {
|
|||||||
}
|
}
|
||||||
// update the fluid amount
|
// update the fluid amount
|
||||||
else {
|
else {
|
||||||
|
//printdbg(this, "> Setting nonzero ($fill) on ($x,$y)")
|
||||||
|
|
||||||
|
setTileTerrain(x, y, Block.WATER) // this function alters fluid list, must be called first // TODO fluidType aware
|
||||||
fluidTypes[addr] = fluidType
|
fluidTypes[addr] = fluidType
|
||||||
fluidFills[addr] = fill
|
fluidFills[addr] = fill
|
||||||
setTileTerrain(x, y, Block.FLUID_MARKER)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*if (x == 60 && y == 256) {
|
||||||
|
printdbg(this, "TileTerrain: ${getTileFromTerrain(x, y)}")
|
||||||
|
printdbg(this, "fluidTypes[$addr] = ${fluidTypes[addr]} (should be ${fluidType.value})")
|
||||||
|
printdbg(this, "fluidFills[$addr] = ${fluidFills[addr]} (should be $fill)")
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFluid(x: Int, y: Int): FluidInfo {
|
fun getFluid(x: Int, y: Int): FluidInfo {
|
||||||
@@ -390,6 +415,7 @@ open class GameWorld {
|
|||||||
@Transient val TERRAIN = 1
|
@Transient val TERRAIN = 1
|
||||||
@Transient val WIRE = 2
|
@Transient val WIRE = 2
|
||||||
|
|
||||||
|
/** 4096 */
|
||||||
@Transient val TILES_SUPPORTED = MapLayer.RANGE * PairedMapLayer.RANGE
|
@Transient val TILES_SUPPORTED = MapLayer.RANGE * PairedMapLayer.RANGE
|
||||||
@Transient val SIZEOF: Byte = MapLayer.SIZEOF
|
@Transient val SIZEOF: Byte = MapLayer.SIZEOF
|
||||||
@Transient val LAYERS: Byte = 4 // terrain, wall (layerTerrainLowBits + layerWallLowBits), wire
|
@Transient val LAYERS: Byte = 4 // terrain, wall (layerTerrainLowBits + layerWallLowBits), wire
|
||||||
@@ -403,4 +429,5 @@ infix fun Float.fmod(other: Float) = if (this >= 0f) this % other else (this % o
|
|||||||
|
|
||||||
inline class FluidType(val value: Int) {
|
inline class FluidType(val value: Int) {
|
||||||
infix fun sameAs(other: FluidType) = this.value.absoluteValue == other.value.absoluteValue
|
infix fun sameAs(other: FluidType) = this.value.absoluteValue == other.value.absoluteValue
|
||||||
|
fun abs() = this.value.absoluteValue
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,6 @@ import net.torvald.terrarum.blockproperties.Block
|
|||||||
import net.torvald.terrarum.console.ConsoleCommand
|
import net.torvald.terrarum.console.ConsoleCommand
|
||||||
import net.torvald.terrarum.console.Echo
|
import net.torvald.terrarum.console.Echo
|
||||||
import net.torvald.terrarum.console.EchoError
|
import net.torvald.terrarum.console.EchoError
|
||||||
import net.torvald.terrarum.modulebasegame.Ingame
|
|
||||||
|
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.util.HashMap
|
import java.util.HashMap
|
||||||
@@ -42,7 +41,7 @@ internal object ExportMap : ConsoleCommand {
|
|||||||
colorTable.put(Block.RAW_TOPAZ, Col4096(0xC70))
|
colorTable.put(Block.RAW_TOPAZ, Col4096(0xC70))
|
||||||
colorTable.put(Block.RAW_AMETHYST, Col4096(0x70C))
|
colorTable.put(Block.RAW_AMETHYST, Col4096(0x70C))
|
||||||
|
|
||||||
colorTable.put(Block.FLUID_MARKER, Col4096(0x038))
|
colorTable.put(Block.WATER, Col4096(0x038))
|
||||||
|
|
||||||
colorTable.put(Block.SAND, Col4096(0xDDB))
|
colorTable.put(Block.SAND, Col4096(0xDDB))
|
||||||
colorTable.put(Block.SAND_WHITE, Col4096(0xFFD))
|
colorTable.put(Block.SAND_WHITE, Col4096(0xFFD))
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package net.torvald.terrarum.modulebasegame.gameworld
|
package net.torvald.terrarum.modulebasegame.gameworld
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
|
import net.torvald.terrarum.AppLoader.printdbg
|
||||||
import net.torvald.terrarum.Terrarum
|
import net.torvald.terrarum.Terrarum
|
||||||
import net.torvald.terrarum.blockproperties.Block
|
import net.torvald.terrarum.blockproperties.Block
|
||||||
import net.torvald.terrarum.roundInt
|
import net.torvald.terrarum.roundInt
|
||||||
@@ -49,6 +50,8 @@ object WorldSimulator {
|
|||||||
private val world = (Terrarum.ingame!!.world)
|
private val world = (Terrarum.ingame!!.world)
|
||||||
|
|
||||||
operator fun invoke(p: ActorHumanoid?, delta: Float) {
|
operator fun invoke(p: ActorHumanoid?, delta: Float) {
|
||||||
|
//printdbg(this, "============================")
|
||||||
|
|
||||||
if (p != null) {
|
if (p != null) {
|
||||||
updateXFrom = p.hitbox.centeredX.div(FeaturesDrawer.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt()
|
updateXFrom = p.hitbox.centeredX.div(FeaturesDrawer.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt()
|
||||||
updateYFrom = p.hitbox.centeredY.div(FeaturesDrawer.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt()
|
updateYFrom = p.hitbox.centeredY.div(FeaturesDrawer.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt()
|
||||||
@@ -58,6 +61,8 @@ object WorldSimulator {
|
|||||||
|
|
||||||
moveFluids(delta)
|
moveFluids(delta)
|
||||||
displaceFallables(delta)
|
displaceFallables(delta)
|
||||||
|
|
||||||
|
//printdbg(this, "============================")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,7 +76,98 @@ object WorldSimulator {
|
|||||||
fun moveFluids(delta: Float) {
|
fun moveFluids(delta: Float) {
|
||||||
makeFluidMapFromWorld()
|
makeFluidMapFromWorld()
|
||||||
|
|
||||||
|
//simCompression()
|
||||||
|
for (y in 1 until fluidMap.size - 1) {
|
||||||
|
for (x in 1 until fluidMap[0].size - 1) {
|
||||||
|
val worldX = x + updateXFrom
|
||||||
|
val worldY = y + updateYFrom
|
||||||
|
|
||||||
|
/*if (worldX == 60 && worldY == 256) {
|
||||||
|
printdbg(this, "tile: ${world.getTileFromTerrain(worldX, worldY)}, isSolid = ${isSolid(worldX, worldY)}")
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if (isSolid(worldX, worldY)) continue
|
||||||
|
val remainingMass = fluidMap[y][x]
|
||||||
|
|
||||||
|
/*if (worldX == 60 && worldY == 256) {
|
||||||
|
printdbg(this, "remainimgMass: $remainingMass at ($worldX, $worldY)")
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if (!isSolid(worldX, worldY + 1)) {
|
||||||
|
fluidNewMap[y][x] -= remainingMass
|
||||||
|
fluidNewMap[y + 1][x] += remainingMass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fluidmapToWorld()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isFlowable(type: FluidType, worldX: Int, worldY: Int): Boolean {
|
||||||
|
val targetFluid = world.getFluid(worldX, worldY)
|
||||||
|
|
||||||
|
// true if target's type is the same as mine, or it's NULL (air)
|
||||||
|
return (targetFluid.type sameAs type || targetFluid.type sameAs Fluid.NULL)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isSolid(worldX: Int, worldY: Int): Boolean {
|
||||||
|
val tile = world.getTileFromTerrain(worldX, worldY)
|
||||||
|
if (tile != Block.WATER) {
|
||||||
|
// check for block properties isSolid
|
||||||
|
return BlockCodex[tile].isSolid
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// check for fluid
|
||||||
|
|
||||||
|
// no STATIC is implement yet, just return false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Explanation of get_stable_state_b (well, kind-of) :
|
||||||
|
|
||||||
|
if x <= 1, all water goes to the lower cell
|
||||||
|
* a = 0
|
||||||
|
* b = 1
|
||||||
|
|
||||||
|
if x > 1 & x < 2*MaxMass + MaxCompress, the lower cell should have MaxMass + (upper_cell/MaxMass) * MaxCompress
|
||||||
|
b = MaxMass + (a/MaxMass)*MaxCompress
|
||||||
|
a = x - b
|
||||||
|
|
||||||
|
->
|
||||||
|
|
||||||
|
b = MaxMass + ((x - b)/MaxMass)*MaxCompress ->
|
||||||
|
b = MaxMass + (x*MaxCompress - b*MaxCompress)/MaxMass
|
||||||
|
b*MaxMass = MaxMass^2 + (x*MaxCompress - b*MaxCompress)
|
||||||
|
b*(MaxMass + MaxCompress) = MaxMass*MaxMass + x*MaxCompress
|
||||||
|
|
||||||
|
* b = (MaxMass*MaxMass + x*MaxCompress)/(MaxMass + MaxCompress)
|
||||||
|
* a = x - b;
|
||||||
|
|
||||||
|
if x >= 2 * MaxMass + MaxCompress, the lower cell should have upper+MaxCompress
|
||||||
|
|
||||||
|
b = a + MaxCompress
|
||||||
|
a = x - b
|
||||||
|
|
||||||
|
->
|
||||||
|
|
||||||
|
b = x - b + MaxCompress ->
|
||||||
|
2b = x + MaxCompress ->
|
||||||
|
|
||||||
|
* b = (x + MaxCompress)/2
|
||||||
|
* a = x - b
|
||||||
|
*/
|
||||||
|
private fun getStableStateB(totalMass: Float): Float {
|
||||||
|
if (totalMass <= 1)
|
||||||
|
return 1f
|
||||||
|
else if (totalMass < 2f * FLUID_MAX_MASS + FLUID_MAX_COMP)
|
||||||
|
return (FLUID_MAX_MASS * FLUID_MAX_MASS + totalMass * FLUID_MAX_COMP) / (FLUID_MAX_MASS + FLUID_MAX_COMP)
|
||||||
|
else
|
||||||
|
return (totalMass + FLUID_MAX_COMP) / 2f
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun simCompression() {
|
||||||
// before data: fluidMap/fluidTypeMap
|
// before data: fluidMap/fluidTypeMap
|
||||||
// after data: fluidNewMap/fluidNewTypeMap
|
// after data: fluidNewMap/fluidNewTypeMap
|
||||||
var flow = 0f
|
var flow = 0f
|
||||||
@@ -156,73 +252,6 @@ object WorldSimulator {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fluidmapToWorld()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isFlowable(type: FluidType, worldX: Int, worldY: Int): Boolean {
|
|
||||||
val targetFluid = world.getFluid(worldX, worldY)
|
|
||||||
|
|
||||||
// true if target's type is the same as mine, or it's NULL (air)
|
|
||||||
return (targetFluid.type sameAs type || targetFluid.type sameAs Fluid.NULL)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isSolid(worldX: Int, worldY: Int): Boolean {
|
|
||||||
val tile = world.getTileFromTerrain(worldX, worldY)
|
|
||||||
if (tile != Block.FLUID_MARKER) {
|
|
||||||
// check for block properties isSolid
|
|
||||||
return BlockCodex[tile].isSolid
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// check for fluid
|
|
||||||
|
|
||||||
// no STATIC is implement yet, just return true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Explanation of get_stable_state_b (well, kind-of) :
|
|
||||||
|
|
||||||
if x <= 1, all water goes to the lower cell
|
|
||||||
* a = 0
|
|
||||||
* b = 1
|
|
||||||
|
|
||||||
if x > 1 & x < 2*MaxMass + MaxCompress, the lower cell should have MaxMass + (upper_cell/MaxMass) * MaxCompress
|
|
||||||
b = MaxMass + (a/MaxMass)*MaxCompress
|
|
||||||
a = x - b
|
|
||||||
|
|
||||||
->
|
|
||||||
|
|
||||||
b = MaxMass + ((x - b)/MaxMass)*MaxCompress ->
|
|
||||||
b = MaxMass + (x*MaxCompress - b*MaxCompress)/MaxMass
|
|
||||||
b*MaxMass = MaxMass^2 + (x*MaxCompress - b*MaxCompress)
|
|
||||||
b*(MaxMass + MaxCompress) = MaxMass*MaxMass + x*MaxCompress
|
|
||||||
|
|
||||||
* b = (MaxMass*MaxMass + x*MaxCompress)/(MaxMass + MaxCompress)
|
|
||||||
* a = x - b;
|
|
||||||
|
|
||||||
if x >= 2 * MaxMass + MaxCompress, the lower cell should have upper+MaxCompress
|
|
||||||
|
|
||||||
b = a + MaxCompress
|
|
||||||
a = x - b
|
|
||||||
|
|
||||||
->
|
|
||||||
|
|
||||||
b = x - b + MaxCompress ->
|
|
||||||
2b = x + MaxCompress ->
|
|
||||||
|
|
||||||
* b = (x + MaxCompress)/2
|
|
||||||
* a = x - b
|
|
||||||
*/
|
|
||||||
private fun getStableStateB(totalMass: Float): Float {
|
|
||||||
if (totalMass <= 1)
|
|
||||||
return 1f
|
|
||||||
else if (totalMass < 2f * FLUID_MAX_MASS + FLUID_MAX_COMP)
|
|
||||||
return (FLUID_MAX_MASS * FLUID_MAX_MASS + totalMass * FLUID_MAX_COMP) / (FLUID_MAX_MASS + FLUID_MAX_COMP)
|
|
||||||
else
|
|
||||||
return (totalMass + FLUID_MAX_COMP) / 2f
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -260,6 +289,8 @@ object WorldSimulator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun makeFluidMapFromWorld() {
|
private fun makeFluidMapFromWorld() {
|
||||||
|
//printdbg(this, "Scan area: ($updateXFrom,$updateYFrom)..(${updateXFrom + fluidMap[0].size},${updateYFrom + fluidMap.size})")
|
||||||
|
|
||||||
for (y in 0 until fluidMap.size) {
|
for (y in 0 until fluidMap.size) {
|
||||||
for (x in 0 until fluidMap[0].size) {
|
for (x in 0 until fluidMap[0].size) {
|
||||||
val fluidData = world.getFluid(x + updateXFrom, y + updateYFrom)
|
val fluidData = world.getFluid(x + updateXFrom, y + updateYFrom)
|
||||||
@@ -267,6 +298,10 @@ object WorldSimulator {
|
|||||||
fluidTypeMap[y][x] = fluidData.type
|
fluidTypeMap[y][x] = fluidData.type
|
||||||
fluidNewMap[y][x] = fluidData.amount
|
fluidNewMap[y][x] = fluidData.amount
|
||||||
fluidNewTypeMap[y][x] = fluidData.type
|
fluidNewTypeMap[y][x] = fluidData.type
|
||||||
|
|
||||||
|
if (x + updateXFrom == 60 && y + updateYFrom == 256) {
|
||||||
|
printdbg(this, "making array amount ${fluidData.amount} for (60,256)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
Let's say you want to do some operation over a 2D array:
|
||||||
|
|
||||||
|
10 04 06 14
|
||||||
|
08 00 02 08
|
||||||
|
02 16 04 12
|
||||||
|
06 02 12 14
|
||||||
|
|
||||||
|
Our task: get the average of px[y][x] and px[y+1][x]
|
||||||
|
|
||||||
|
Single-threaded
|
||||||
|
|
||||||
|
09 02 04 11
|
||||||
|
05 08 03 10
|
||||||
|
04 09 08 13
|
||||||
|
06 02 12 14 (Observation: can work with "scrachpad", but also inline mutable)
|
||||||
|
|
||||||
|
4 threads
|
||||||
|
|
||||||
|
09 02 04 11 .. .. .. .. .. .. .. .. .. .. .. .. These are single scrachpad but
|
||||||
|
.. .. .. .. 05 08 03 10 .. .. .. .. .. .. .. .. threads are writing to un-shared
|
||||||
|
.. .. .. .. .. .. .. .. 04 09 08 13 .. .. .. .. portion of the array
|
||||||
|
.. .. .. .. .. .. .. .. .. .. .. .. 06 02 12 14
|
||||||
|
|
||||||
|
(Observation: "scrachpad" is a must, making original array immutable, this multithreading works)
|
||||||
Reference in New Issue
Block a user