A commit to save my soul as I'm going to fuck up things a bit...

This commit is contained in:
Song Minjae
2017-04-30 21:26:20 +09:00
parent c9c761598a
commit 1375f91505
7 changed files with 508 additions and 182 deletions

132
ABOUT.md
View File

@@ -1,132 +0,0 @@
## About gamemaking ##
### CHALLENGING, NOT PUNISHING ###
https://www.youtube.com/watch?v=ea6UuRTjkKs
1. CONSISTENT RULES
- No arbitrary unstoppable death
2. Player's skill involved
- Can play around, not restart
3. Usability of in-game tools
- Players should be able to 'regret' their strategy and adjust.
4. Comfortable control
5. Make players overcome the challenge, not defeating them
6. Let players have "aha" moment when they failed.
- Make them hungry to retry with new strategies.
- Some small things they've could done differently
- e.g. "One-big-hit didn't worked, may I should've picked up high DPS one"
### MORE DEPTH, LESS COMPLEXITY ###
https://www.youtube.com/watch?v=jVL4st0blGU
1. Memorise less!
- Less burden to, even starting the game
- Start with gentle learning curve, getting slowly steep
- Intuitive UX (UI, control, ...)
- Good tutorial = lessens complexity
2. Intuitive!
3. Calculations per second
- reduce!
4. Players have to know everything to even begin the play ### FAIL (irreducible complexity)
- Make them get familiar with rules of the game
- Dwarf Fortress failed this!
### Lots of things players can play with (aka don't make them bored) ###
- Combat, battle, building, mechanics, adventure, dungeon explore, spelunking
- Not scaled; easy combat, tough combat, tedious combat, etc.
### Achieving perfect imbalance ###
https://www.youtube.com/watch?v=e31OSVZF77w
- Make sure no matter how you skilled, your playable character cannot be good at everything
- Give players __wide pool of options__ to solve problem
(kill the boss, defend their adobe, fast transportation, etc.)
**_What feeling do you want to convey?_**
### Always think WHY you want to add _something_ on the game ###
- e.g. Why are you adding RPG leveling system? What it would do to the players? How would they play with?
See also: *HEARTS, CLUBS, DIAMONDS, SPADES: PLAYERS WHO SUIT MUDS*
## About this very game ##
### Friendlier version of Dwarf Fortress Adventure mode ###
- Yet _lots of fun_
- Add Fortress mode features by 'make your own settling'
- Hard to actually die, but once you die, you're done.
+ Config: imtooyoungtodie for easy mode
- Genre: Adventure, Open world (towns in RPG, building, town managing (conquer existing one or
you build one and persuade existing people to move in) -> See Dwarf Fortress and Animal Crossing)
* Adventure: adventure this vast—5,5 km wide—world, discover new (and good/horrible) things
* Open world:
- Building: building your own houses, structures, etc.
- Town managing:
1. Build your own little hamlet and manage it
or-
2. Conquer existing one and become a ruler
The town is a special hamlet that can be tailored for your taste
- Survival:
mobs will trying to attack your assets (yourself, your hamlet, your people)
### Side view ###
### Interact menu w/ mouse right ###
### Pixelated sprites ###
- Use 2x sprites if rotating does not work well
### User experience ###
* Indicative mouse cursor
### Game mechanics ###
* 24 pixels == 1 metre
### Purpose of the game ###
* Boss
- Will be mentioned/shown as absolute _evil_.
- But actually is not.
* Theme
- Is an evil really really is what we think?
- Is there a thing as 'absolute evil'?
* Boss character
- From debugger character
- Name key: "Sigriðr hinn Dróttningin" (can be changed)
* Little setting
- A ruler, hated by people
* Mechanics
- Beating boss does not ends the game, but grants an ability to
create new character as it.

View File

@@ -33,8 +33,8 @@ class TestPick extends GameItem {
boolean stackable = true
int maxDurability = 147
float durability = maxDurability
int equipPosition = EquipPosition.HAND_GRIP
String inventoryCategory = Category.TOOL
int equipPosition = 9 //EquipPosition.HAND_GRIP
String inventoryCategory = "tool" //Category.TOOL
// !! TEST MATERIAL !!
Material material = new Material(0,0,0,0,0,0,0,0,1,0.0)

View File

@@ -0,0 +1,211 @@
import java.io.InputStream
import java.io.OutputStream
/**
* Just to make things slow down
*
* This version of Brainfuck fills memory with sanitised input program, and initialises
* memory pointer to be just right after your input program. This brings three major improvements:
*
* 1. Possibility of Self-modifying code
* 2. Fucks your brain even more
* 3. Forces you to enhance your calm
*
* Also note that program counter and memory pointer will wrap around when commands are executed,
* but not when program is being loaded (will throw OutOfMemoryException).
*
* If memory at Program Counter is equal to 0xFF, it is interpreted as termination. (0xFF is NOT a
* valid opcode for input program, however)
*
* Created by minjaesong on 17-04-29.
*/
class BFVM(
val memSize: Int = 65536,
val stdout: OutputStream = System.out,
val stdin: InputStream = System.`in`
) {
private val ZERO = 0.toByte()
private val INP = '>'.toByte()
private val DEP = '<'.toByte()
private val INC = '+'.toByte()
private val DEC = '-'.toByte()
private val PRN = '.'.toByte()
private val RDI = ','.toByte()
private val JPZ = '['.toByte()
private val JPN = ']'.toByte()
private val CYA = 0xFF.toByte()
private val bfOpcodes = hashSetOf<Byte>(43,44,45,46,60,62,91,93)
private val instSet = hashMapOf<Byte, () -> Unit>(
Pair(INP, { INP() }),
Pair(DEP, { DEP() }),
Pair(INC, { INC() }),
Pair(DEC, { DEC() }),
Pair(PRN, { PRN() }),
Pair(RDI, { RDI() }),
Pair(JPZ, { JPZ() }),
Pair(JPN, { JPN() })
)
private var r1: Byte = ZERO // Register One (Data register)
private var r2 = 0 // Register Two (Scratchpad); theoretically I can use R1 but it limits bracket depth to 254
private var mp = 0 // Memory Pointer
private var pc = 0 // Program Counter
private var ir = 0 // Instruction Register; does lookahead ahd lookbehind
private val mem = ByteArray(memSize)
/*
Input program is loaded into the memory from index zero.
Interrupts are hard-coded, 'cause why not?
Code Mnemo. Desc.
----|------|-----
INP > Increment pointer
DEP < Decrement pointer
INC + Increment memory
DEC - Decrement memory
PRN . Print as text
RDI , Read from input
JPZ [ Jump past to matching ] when mem is zero
JPN ] Jump back to matching [ when mem is non-zero
[ Internal operations ]
CYA 0xFF Marks end of the input program
*/
// NOTE: INC_PC is implied
private fun INP() {
INC_MP()
}
private fun DEP() {
DEC_MP()
}
private fun INC() {
r1 = mem[mp]
r1++
mem[mp] = r1
}
private fun DEC() {
r1 = mem[mp]
r1--
mem[mp] = r1
}
private fun PRN() {
stdout.write(mem[mp].toInt())
}
private fun RDI() {
r1 = stdin.read().toByte()
mem[mp] = r1
}
private fun JPZ() {
if (mem[mp] == ZERO) {
// lookahead
ir = pc
r2 = 0
while (r2 != -1) {
INC_IR()
if (JPZ == mem[ir]) {
r2++
}
else if (JPN == mem[ir]) {
r2--
}
}
pc = ir
}
}
private fun JPN() {
if (mem[mp] != ZERO) {
// lookbehind
ir = pc
r2 = 0
while (r2 != -1) {
DEC_IR()
if (JPN == mem[ir]) {
r2++
}
else if (JPZ == mem[ir]) {
r2--
}
}
pc = ir
}
}
// END OF NOTE (INC_PC is implied)
fun execute() {
while (mem[pc] != CYA) {
//println("pc = $pc, mp = $mp, inst = ${mem[pc].toChar()}, mem = ${mem[mp]}")
instSet[mem[pc]]?.invoke() // fetch-decode-execute in one line
INC_PC()
}
}
fun loadProgram(program: String) {
val program = program.toByteArray(charset = Charsets.US_ASCII)
pc = 0 // FOR NOW it's PC for input program
mp = 0 // where to dump input bytes
while (pc < program.size) {
if (pc >= memSize - 1) {
throw OutOfMemoryError("Virtual Machine Out of Memory")
}
r1 = program[pc]
if (r1 in bfOpcodes) {
mem[mp] = r1
INC_MP()
}
INC_PC()
}
mem[program.size] = CYA
mp = (program.size + 1) mod memSize
pc = 0
ir = 0
}
private fun INC_PC() { pc = (pc + 1) mod memSize }
private fun INC_IR() { ir = (ir + 1) mod memSize }
private fun DEC_IR() { ir = (ir - 1) mod memSize }
private fun INC_MP() { mp = (mp + 1) mod memSize }
private fun DEC_MP() { mp = (mp - 1) mod memSize }
private infix fun Int.mod(other: Int) = Math.floorMod(this, other)
}
val vm = BFVM()
val factorials = """
+++++++++++
>+>>>>++++++++++++++++++++++++++++++++++++++++++++
>++++++++++++++++++++++++++++++++<<<<<<[>[>>>>>>+>
+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]<[>++++++++++[-
<-[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]]>[<<[>>>+<<<
-]>>[-]]<<]>>>[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]]
>[<<+>>[-]]<<<<<<<]>>>>>[+++++++++++++++++++++++++
+++++++++++++++++++++++.[-]]++++++++++<[->-<]>++++
++++++++++++++++++++++++++++++++++++++++++++.[-]<<
<<<<<<<<<<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]<-[>>.>.<<<
[-]]<<[>>+>+<<<-]>>>[<<<+>>>-]<<[<+>-]>[<+>-]<<<-]
"""
vm.loadProgram(factorials)
vm.execute()

View File

@@ -25,6 +25,11 @@ class BFVM(
val stdout: OutputStream = System.out,
val stdin: InputStream = System.`in`
) {
annotation class Unsigned
private val DEBUG = true
private val ZERO = 0.toByte()
private val INP = '>'.toByte()
@@ -35,8 +40,16 @@ class BFVM(
private val RDI = ','.toByte()
private val JPZ = '['.toByte()
private val JPN = ']'.toByte()
private val CYA = 0xFF.toByte()
private val LDZ = '0'.toByte()
private val ADM = 'M'.toByte()
private val ADP = 'P'.toByte()
private val SBM = 'm'.toByte()
private val SBP = 'p'.toByte()
private val bfOpcodes = hashSetOf<Byte>(43,44,45,46,60,62,91,93)
private val instSet = hashMapOf<Byte, () -> Unit>(
@@ -47,9 +60,16 @@ class BFVM(
Pair(PRN, { PRN() }),
Pair(RDI, { RDI() }),
Pair(JPZ, { JPZ() }),
Pair(JPN, { JPN() })
Pair(JPN, { JPN() }),
Pair(LDZ, { LDZ() })
)
private val instOneArg = hashMapOf<@Unsigned Byte, (Int) -> Unit>(
Pair(ADM, { i -> ADM(i) }),
Pair(ADP, { i -> ADP(i) }),
Pair(SBM, { i -> SBM(i) }),
Pair(SBP, { i -> SBP(i) })
)
private var r1: Byte = ZERO // Register One (Data register)
private var r2 = 0 // Register Two (Scratchpad); theoretically I can use R1 but it limits bracket depth to 254
@@ -78,6 +98,14 @@ class BFVM(
[ Internal operations ]
CYA 0xFF Marks end of the input program
[ Optimise operations ]
LDZ 0 Set memory to zero
ADM M Add immediate to memory (RLEing +)
ADP P Add immediate to memory pointer (RLEing >)
SBM m Subtract immediate to memory (RLEing -)
SBP p Subtract immediate to memory pointer (RLEing <)
*/
// NOTE: INC_PC is implied
@@ -98,7 +126,7 @@ class BFVM(
mem[mp] = r1
}
private fun PRN() {
stdout.write(mem[mp].toInt())
stdout.write(mem[mp].toUint())
}
private fun RDI() {
r1 = stdin.read().toByte()
@@ -111,7 +139,7 @@ class BFVM(
r2 = 0
while (r2 != -1) {
INC_IR()
ir++
if (JPZ == mem[ir]) {
r2++
}
@@ -130,7 +158,7 @@ class BFVM(
r2 = 0
while (r2 != -1) {
DEC_IR()
ir--
if (JPN == mem[ir]) {
r2++
}
@@ -142,22 +170,67 @@ class BFVM(
pc = ir
}
}
// non-standard
private fun LDZ() {
mem[mp] = 0
}
private fun ADM(i: Int) {
mem[mp] = (mem[mp] + i).toByte()
}
private fun SBM(i: Int) {
mem[mp] = (mem[mp] - i).toByte()
}
private fun ADP(i: Int) {
mp = (mp + i) mod memSize
}
private fun SBP(i: Int) {
mp = (mp - i) mod memSize
}
// END OF NOTE (INC_PC is implied)
fun execute() {
dbgp("Now run...")
while (mem[pc] != CYA) {
//println("pc = $pc, mp = $mp, inst = ${mem[pc].toChar()}, mem = ${mem[mp]}")
instSet[mem[pc]]?.invoke() // fetch-decode-execute in one line
//dbgp("pc = $pc, mp = $mp, inst = ${mem[pc].toChar()} ${mem[pc+1]}, mem = ${mem[mp]}")
r1 = mem[pc]
if (r1 in instSet) {
instSet[r1]!!.invoke() // fetch-decode-execute in one line
}
else if (r1 in instOneArg) {
INC_PC()
r2 = mem[pc].toUint()
instOneArg[r1]?.invoke(r2)
}
else {
dbgp("invalid: $r1")
}
INC_PC()
}
}
fun loadProgram(program: String) {
fun loadProgram(program: String, optimizeLevel: Int = NO_OPTIMISE) {
dbgp("Now load...")
fun putOp(op: Byte) {
//dbgp("${op.toChar()} ${op.toUint()}")
mem[mp] = op
INC_MP()
}
val program = program.toByteArray(charset = Charsets.US_ASCII)
r1 = 0 // currently reading operation
pc = 0 // FOR NOW it's PC for input program
mp = 0 // where to dump input bytes
r2 = 0 // scratchpad
ir = 0 // lookahead pointer
while (pc < program.size) {
if (pc >= memSize - 1) {
@@ -167,62 +240,109 @@ class BFVM(
r1 = program[pc]
if (r1 in bfOpcodes) {
mem[mp] = r1
INC_MP()
if (optimizeLevel >= 1) {
// [-]
if (r1 == JPZ) {
if (program[pc + 1] == DEC && program[pc + 2] == JPN) {
pc += 3
putOp(LDZ)
continue
}
}
// RLEing +
else if (INC == r1 && program[pc + 1] == r1) {
ir = 2
while (INC == program[pc + ir] && ir < 255) ir++
pc += ir
putOp(ADM)
putOp(ir.toByte())
continue
}
// RLEing -
else if (DEC == r1 && program[pc + 1] == r1) {
ir = 2
while (DEC == program[pc + ir] && ir < 255) ir++
pc += ir
putOp(SBM)
putOp(ir.toByte())
continue
}
// RLEing >
else if (INP == r1 && program[pc + 1] == r1) {
ir = 2
while (INP == program[pc + ir] && ir < 255) ir++
pc += ir
putOp(ADP)
putOp(ir.toByte())
continue
}
// RLEing <
else if (DEP == r1 && program[pc + 1] == r1) {
ir = 2
while (DEP == program[pc + ir] && ir < 255) ir++
pc += ir
putOp(SBP)
putOp(ir.toByte())
continue
}
}
putOp(r1)
}
INC_PC()
pc += 1
}
mem[program.size] = CYA
mp = (program.size + 1) mod memSize
mem[mp] = CYA
dbgp("Prg size: $mp")
INC_MP()
pc = 0
ir = 0
}
private fun INC_PC() { pc = (pc + 1) mod memSize }
private fun INC_IR() { ir = (ir + 1) mod memSize }
private fun DEC_IR() { ir = (ir - 1) mod memSize }
private fun INC_MP() { mp = (mp + 1) mod memSize }
private fun DEC_MP() { mp = (mp - 1) mod memSize }
private infix fun Int.mod(other: Int) = Math.floorMod(this, other)
private fun Byte.toUint() = java.lang.Byte.toUnsignedInt(this)
private fun dbgp(s: Any) {
if (DEBUG) println(s)
}
val NO_OPTIMISE = 0
/*
Optimise level
1 RLE, Set cell to zero
*/
}
val vm = BFVM()
val factorials = """
github (dot) com/saulpw/brainfuck/blob/master/tests/facto (dot) b
++++++++++>>>+>>>>+>+<[[+++++[>++++
++++<-]>.<++++++[>--------<-]+<<]<<
[<<]<.>>>>+<[->[<+>-[<+>-[<+>-[<+>-
[<+>-[<+>-[<+>-[<+>-[<+>-[<[-]>-+>[
<->-]<[->>>[>>]<<[->[>>+<<-]>+<<<<]
<]>[-]+>+<<]]]]]]]]]]<[>+<-]+>>]<<[
<<]>>[->>[>>]>>[-<<[<<]<<[<<]>[>[>>
]>>[>>]>>[>>]>+>+<<<<[<<]<<[<<]<<[<
<]>-]>[>>]>>[>>]>>[>>]>[<<<[<<]<<[<
<]<<[<<]>+>[>>]>>[>>]>>[>>]>-]<<<[<
<]>[>[>>]>+>>+<<<<<[<<]>-]>[>>]>[<<
<[<<]>+>[>>]>-]>>[<[<<+>+>-]<[>>>+<
<<-]<[>>+<<-]>>>-]<[-]>>+[>[>>>>]>[
>>>>]>[-]+>+<[<<<<]>-]>[>>>>]>[>>>>
]>->-[<<<+>>+>-]<[>+<-]>[[<<+>+>-]<
[<->-[<->-[<->-[<->-[<->-[<->-[<->-
[<->-[<->-[<-<---------->>[-]>>>>[-
]+>+<<<<<]]]]]]]]]]<[>>+<<-]>>]<+<+
<[>>>+<<<-]<<[<<<<]<<<<<[<<]+>>]>>>
>>[>>>>]+>[>>>>]<<<<[-<<<<]>>>>>[<<
<<]<<<<<[<<]<<[<<]+>>]>>[>>]>>>>>[-
>>>>]<<[<<<<]>>>>[>>>>]<<<<<<<<[>>>
>[<<+>>->[<<+>>-]>]<<<<[<<]<<]<<<<<
[->[-]>>>>>>>>[<<+>>->[<<+>>-]>]<<<
<[<<]<<<<<<<]>>>>>>>>>[<<<<<<<+>>>>
>>>->[<<<<<<<+>>>>>>>-]>]<<<<<<<<<]
+++++++++++
>+>>>>++++++++++++++++++++++++++++++++++++++++++++
>++++++++++++++++++++++++++++++++<<<<<<[>[>>>>>>+>
+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]<[>++++++++++[-
<-[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]]>[<<[>>>+<<<
-]>>[-]]<<]>>>[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]]
>[<<+>>[-]]<<<<<<<]>>>>>[+++++++++++++++++++++++++
+++++++++++++++++++++++.[-]]++++++++++<[->-<]>++++
++++++++++++++++++++++++++++++++++++++++++++.[-]<<
<<<<<<<<<<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]<-[>>.>.<<<
[-]]<<[>>+>+<<<-]>>>[<<<+>>>-]<<[<+>-]>[<+>-]<<<-]
"""
vm.loadProgram(factorials)
vm.loadProgram(factorials, optimizeLevel = 1)
vm.execute()

View File

@@ -834,9 +834,9 @@ class StateInGame : BasicGameState() {
}
}
private fun ArrayList<out Actor>.binarySearch(actor: Actor) = this.binarySearch(actor.referenceID)
private fun ArrayList<*>.binarySearch(actor: Actor) = this.binarySearch(actor.referenceID)
private fun ArrayList<out Actor>.binarySearch(ID: Int): Int {
private fun ArrayList<*>.binarySearch(ID: Int): Int {
// code from collections/Collections.kt
var low = 0
var high = this.size - 1
@@ -844,11 +844,11 @@ class StateInGame : BasicGameState() {
while (low <= high) {
val mid = (low + high).ushr(1) // safe from overflows
val midVal = get(mid)
val midVal = get(mid)!!
if (ID > midVal.referenceID)
if (ID > midVal.hashCode())
low = mid + 1
else if (ID < midVal.referenceID)
else if (ID < midVal.hashCode())
high = mid - 1
else
return mid // key found

View File

@@ -0,0 +1,61 @@
## Friendlier version of Dwarf Fortress Adventure mode
- Yet lots of !!fun!! (wat that double exclamation mark!)
- You can build your own hamlet (Dwarf "Fortress" reference)
- You die, you lose "you"; the world will be there, some of your possession will be on the ground, but you got to start over by making new character from scratch
- Unless you have built Resurrection Shrine, which you won't going to easily build it
- Genre: Adventure, Open world (towns in RPG, building, town managing (conquer existing one or
you build one and persuade existing people to move in) -> See Dwarf Fortress and Animal Crossing)
* Adventure: adventure this vast—5,5 km wide—world, discover new (and good/horrible) things
* Open world:
- Building: building your own houses, structures, etc.
- Town managing:
1. Build your own little hamlet and manage it
or-
2. Conquer existing one and become a ruler
The town is a special hamlet that can be tailored for your taste
- Survival:
mobs will trying to attack your assets (yourself, your hamlet, your people)
## Side view
## Interact menu w/ mouse right
## User experience
* Indicative mouse cursor
## Game mechanics
* 24 pixels == 1 metre
## Purpose of the game
* Boss
- Will be mentioned/shown as absolute _evil_.
- But actually is not.
* Theme
- Is an evil really really is what we think?
- Is there a thing as 'absolute evil'?
* Boss character
- From debugger character
- Name key: "Sigriðr hinn Dróttningin" (can be changed)
* Little setting
- A ruler, hated by people
* Mechanics
- Beating boss does not ends the game, but grants an ability to
create new character as it.
...We need to re-think about it. —Torvald, 2017-04-30

View File

@@ -0,0 +1,66 @@
(Warning — this article refers Extra Credits bit too much...)
## CHALLENGING, NOT PUNISHING
https://www.youtube.com/watch?v=ea6UuRTjkKs
1. CONSISTENT RULES
- No arbitrary unstoppable death
2. Player's skill involved
- Can play around, not restart
3. Usability of in-game tools
- Players should be able to 'regret' their strategy and adjust.
4. Comfortable control
5. Make players overcome the challenge, not defeating them
6. Let players have "aha" moment when they failed.
- Make them hungry to retry with new strategies.
- Some small things they've could done differently
- e.g. "One-big-hit didn't worked, may I should've picked up high DPS one"
## MORE DEPTH, LESS COMPLEXITY
https://www.youtube.com/watch?v=jVL4st0blGU
1. Memorise less!
- Less burden to, even starting the game
- Start with gentle learning curve, getting slowly steep
- Intuitive UX (UI, control, ...)
- Good tutorial = lessens complexity
2. Intuitive!
3. Calculations per second
- reduce!
4. Players have to know everything to even begin the play ### FAIL (irreducible complexity)
- Make them get familiar with rules of the game
- Dwarf Fortress failed this!
## Lots of things players can play with (aka don't make them bored)
- Combat, battle, building, mechanics, adventure, dungeon explore, spelunking
- Not scaled; easy combat, tough combat, tedious combat, etc.
## Achieving perfect imbalance
https://www.youtube.com/watch?v=e31OSVZF77w
- Make sure no matter how you skilled, your playable character cannot be good at everything
- Give players __wide pool of options__ to solve problem
(kill the boss, defend their adobe, fast transportation, etc.)
**_What feeling do you want to convey?_**
## Always think WHY you want to add _something_ on the game
- e.g. Why are you adding RPG leveling system? What it would do to the players? How would they play with?
See also: *HEARTS, CLUBS, DIAMONDS, SPADES: PLAYERS WHO SUIT MUDS*