Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e98abcdda7 |
@@ -1,17 +0,0 @@
|
||||
sdl_video = "main"
|
||||
[dependencies]
|
||||
extramath = "https://gitea.pockle.world/john/extramath@master"
|
||||
[system]
|
||||
ar_timer = 60
|
||||
actor_memory = 0
|
||||
net_service = 0.1
|
||||
reply_timeout = 60
|
||||
actor_max = "10_000"
|
||||
stack_max = 0
|
||||
[actors]
|
||||
[actors.prosperon/sdl_video]
|
||||
main = true
|
||||
[actors.prosperon/prosperon]
|
||||
main = true
|
||||
[actors.prosperon]
|
||||
main = true
|
||||
@@ -1,6 +0,0 @@
|
||||
[modules]
|
||||
[modules.extramath]
|
||||
hash = "MCLZT3JABTAENS4WVXKGWJ7JPBLZER4YQ5VN2PE7ZD2Z4WYGTIMA===="
|
||||
url = "https://gitea.pockle.world/john/extramath@master"
|
||||
downloaded = "Monday June 2 12:07:20.42 PM -5 2025 AD"
|
||||
commit = "84d81a19a8455bcf8dc494739e9e6d545df6ff2c"
|
||||
13
.gitignore
vendored
@@ -6,18 +6,27 @@ build/
|
||||
*.o
|
||||
*.a
|
||||
*.d
|
||||
tags
|
||||
Jenkinsfile
|
||||
*~
|
||||
*.log
|
||||
*.gz
|
||||
*.tar
|
||||
.nova/
|
||||
packer*
|
||||
primum
|
||||
sokol-shdc*
|
||||
source/shaders/*.h
|
||||
core.cdb
|
||||
primum.exe
|
||||
core.cdb.h
|
||||
jsc
|
||||
.DS_Store
|
||||
*.html
|
||||
.vscode
|
||||
*.icns
|
||||
game.zip
|
||||
icon.ico
|
||||
steam/
|
||||
subprojects/*/
|
||||
build_dbg/
|
||||
modules/
|
||||
build_dbg/
|
||||
20
.moth/log
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"console": {
|
||||
"outputs": ["console"]
|
||||
},
|
||||
"info": {
|
||||
"outputs": ["file:logs/info.log"]
|
||||
},
|
||||
"warning": {
|
||||
"outputs": ["console", "file:logs/warnings.log"]
|
||||
},
|
||||
"error": {
|
||||
"outputs": ["console", "file:logs/errors.log", "actor:localhost:5678"]
|
||||
},
|
||||
"debug": {
|
||||
"outputs": ["file:logs/debug.log"]
|
||||
},
|
||||
"audit": {
|
||||
"outputs": ["file:logs/audit.log", "actor:audit-server:9000"]
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ This is a game engine developed using a QuickJS fork as its scripting language.
|
||||
|
||||
## Coding Practices
|
||||
- Use K&R style C
|
||||
- Use as little whitespace as possible
|
||||
- Javascript style prefers objects and prototypical inheritence over ES6 classes, liberal use of closures, and var everywhere
|
||||
|
||||
## Instructions
|
||||
|
||||
21
CLAUDE.md
@@ -5,7 +5,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
## Build Commands
|
||||
|
||||
### Build variants
|
||||
- `make` - Make and install debug version. Usually all that's needed.
|
||||
- `make debug` - Build debug version (uses meson debug configuration)
|
||||
- `make fast` - Build optimized version
|
||||
- `make release` - Build release version with LTO and optimizations
|
||||
- `make small` - Build minimal size version
|
||||
@@ -13,10 +13,10 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
- `make crosswin` - Cross-compile for Windows using mingw32
|
||||
|
||||
### Testing
|
||||
After install with 'make', just run 'cell' and point it at the actor you want to launch. "cell tests/toml" runs the actor "tests/toml.js"
|
||||
|
||||
## Scripting language
|
||||
This is called "cell", but it is is a variant of javascript and extremely similar.
|
||||
- `meson test -C build_dbg` - Run all tests in debug build
|
||||
- `meson test -C build_<variant>` - Run tests in specific build variant
|
||||
- `./build_dbg/prosperon tests/<testname>.js` - Run specific test
|
||||
- Available tests: `spawn_actor`, `empty`, `nota`, `wota`, `portalspawner`, `overling`, `send`, `delay`
|
||||
|
||||
### Common development commands
|
||||
- `meson setup build_<variant>` - Configure build directory
|
||||
@@ -126,7 +126,7 @@ meson test -C build_dbg
|
||||
### Debugging
|
||||
- Use debug build: `make debug`
|
||||
- Tracy profiler support when enabled
|
||||
- Console logging available via `log.console()`, `log.error()`, etc.
|
||||
- Console logging available via `console.log()`, `console.error()`, etc.
|
||||
- Log files written to `.prosperon/log.txt`
|
||||
|
||||
# Project Structure Notes
|
||||
@@ -202,11 +202,6 @@ meson test -C build_dbg
|
||||
|
||||
### Utility Modules
|
||||
- `time` - Time management and delays
|
||||
- **Must be imported with `use('time')`**
|
||||
- No `time.now()` function - use:
|
||||
- `time.number()` - Number representation of current time
|
||||
- `time.record()` - Struct representation of current time
|
||||
- `time.text()` - Text representation of current time
|
||||
- `io` - File I/O operations
|
||||
- `json` - JSON parsing and serialization
|
||||
- `util` - General utilities
|
||||
@@ -239,7 +234,7 @@ When sending a message with a callback, respond by sending to the message itself
|
||||
```javascript
|
||||
// Sender side:
|
||||
send(actor, {type: 'status'}, response => {
|
||||
log.console(response); // Handle the response
|
||||
console.log(response); // Handle the response
|
||||
});
|
||||
|
||||
// Receiver side:
|
||||
@@ -284,7 +279,7 @@ $_.receiver(msg => {
|
||||
- Custom formats: Aseprite animations, etc.
|
||||
|
||||
### Developer Tools
|
||||
- Built-in documentation system with `cell.DOC`
|
||||
- Built-in documentation system with `prosperon.DOC`
|
||||
- Tracy profiler integration for performance monitoring
|
||||
- Imgui debugging tools
|
||||
- Console logging with various severity levels
|
||||
|
||||
12
Makefile
@@ -1,22 +1,22 @@
|
||||
debug: FORCE
|
||||
meson setup build_dbg -Dbuildtype=debugoptimized
|
||||
meson install --only-changed -C build_dbg
|
||||
meson setup build_dbg -Dbuildtype=debug
|
||||
meson compile -C build_dbg
|
||||
|
||||
fast: FORCE
|
||||
meson setup build_fast
|
||||
meson install -C build_fast
|
||||
meson compile -C build_fast
|
||||
|
||||
release: FORCE
|
||||
meson setup -Dbuildtype=release -Db_lto=true -Db_lto_mode=thin -Db_ndebug=true build_release
|
||||
meson install -C build_release
|
||||
meson compile -C build_release
|
||||
|
||||
sanitize: FORCE
|
||||
meson setup -Db_sanitize=address -Db_sanitize=memory -Db_sanitize=leak -Db_sanitize=undefined build_sani
|
||||
meson install -C build_sani
|
||||
meson compile -C build_sani
|
||||
|
||||
small: FORCE
|
||||
meson setup -Dbuildtype=minsize -Db_lto=true -Db_ndebug=true build_small
|
||||
meson install -C build_small
|
||||
meson compile -C build_small
|
||||
|
||||
web: FORCE
|
||||
meson setup -Deditor=false -Dbuildtype=minsize -Db_lto=true -Db_ndebug=true --cross-file emscripten.cross build_web
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
function mainThread() {
|
||||
var maxDepth = Math.max(6, Number(arg[0] || 16));
|
||||
|
||||
var stretchDepth = maxDepth + 1;
|
||||
var check = itemCheck(bottomUpTree(stretchDepth));
|
||||
log.console(`stretch tree of depth ${stretchDepth}\t check: ${check}`);
|
||||
|
||||
var longLivedTree = bottomUpTree(maxDepth);
|
||||
|
||||
for (let depth = 4; depth <= maxDepth; depth += 2) {
|
||||
var iterations = 1 << maxDepth - depth + 4;
|
||||
work(iterations, depth);
|
||||
}
|
||||
|
||||
log.console(`long lived tree of depth ${maxDepth}\t check: ${itemCheck(longLivedTree)}`);
|
||||
}
|
||||
|
||||
function work(iterations, depth) {
|
||||
let check = 0;
|
||||
for (let i = 0; i < iterations; i++)
|
||||
check += itemCheck(bottomUpTree(depth));
|
||||
log.console(`${iterations}\t trees of depth ${depth}\t check: ${check}`);
|
||||
}
|
||||
|
||||
function TreeNode(left, right) {
|
||||
return {left, right};
|
||||
}
|
||||
|
||||
function itemCheck(node) {
|
||||
if (node.left == null)
|
||||
return 1;
|
||||
return 1 + itemCheck(node.left) + itemCheck(node.right);
|
||||
}
|
||||
|
||||
function bottomUpTree(depth) {
|
||||
return depth > 0
|
||||
? new TreeNode(bottomUpTree(depth - 1), bottomUpTree(depth - 1))
|
||||
: new TreeNode(null, null);
|
||||
}
|
||||
|
||||
mainThread()
|
||||
|
||||
$_.stop()
|
||||
@@ -1,24 +0,0 @@
|
||||
var blob = use('blob')
|
||||
|
||||
function eratosthenes (n) {
|
||||
var sieve = new blob(n, true)
|
||||
var sqrtN = Math.trunc(Math.sqrt(n));
|
||||
|
||||
for (i = 2; i <= sqrtN; i++)
|
||||
if (sieve.read_logical(i))
|
||||
for (j = i * i; j <= n; j += i)
|
||||
sieve.write_bit(j, false);
|
||||
|
||||
return sieve;
|
||||
}
|
||||
|
||||
var sieve = eratosthenes(10000000);
|
||||
stone(sieve)
|
||||
|
||||
var c = 0
|
||||
for (var i = 0; i < sieve.length; i++)
|
||||
if (sieve.read_logical(i)) c++
|
||||
|
||||
log.console(c)
|
||||
|
||||
$_.stop()
|
||||
@@ -1,58 +0,0 @@
|
||||
function fannkuch(n) {
|
||||
var perm1 = [n]
|
||||
for (let i = 0; i < n; i++) perm1[i] = i
|
||||
var perm = [n]
|
||||
var count = [n]
|
||||
var f = 0, flips = 0, nperm = 0, checksum = 0
|
||||
var i, k, r
|
||||
|
||||
r = n
|
||||
while (r > 0) {
|
||||
i = 0
|
||||
while (r != 1) { count[r-1] = r; r -= 1 }
|
||||
while (i < n) { perm[i] = perm1[i]; i += 1 }
|
||||
|
||||
// Count flips and update max and checksum
|
||||
f = 0
|
||||
k = perm[0]
|
||||
while (k != 0) {
|
||||
i = 0
|
||||
while (2*i < k) {
|
||||
let t = perm[i]; perm[i] = perm[k-i]; perm[k-i] = t
|
||||
i += 1
|
||||
}
|
||||
k = perm[0]
|
||||
f += 1
|
||||
}
|
||||
if (f > flips) flips = f
|
||||
if ((nperm & 0x1) == 0) checksum += f; else checksum -= f
|
||||
|
||||
// Use incremental change to generate another permutation
|
||||
var more = true
|
||||
while (more) {
|
||||
if (r == n) {
|
||||
log.console( checksum )
|
||||
return flips
|
||||
}
|
||||
let p0 = perm1[0]
|
||||
i = 0
|
||||
while (i < r) {
|
||||
let j = i + 1
|
||||
perm1[i] = perm1[j]
|
||||
i = j
|
||||
}
|
||||
perm1[r] = p0
|
||||
|
||||
count[r] -= 1
|
||||
if (count[r] > 0) more = false; else r += 1
|
||||
}
|
||||
nperm += 1
|
||||
}
|
||||
return flips;
|
||||
}
|
||||
|
||||
var n = arg[0] || 10
|
||||
|
||||
log.console(`Pfannkuchen(${n}) = ${fannkuch(n)}`)
|
||||
|
||||
$_.stop()
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Run hyperfine with parameter lists
|
||||
# This will create a cross-product of all libraries × all scenarios
|
||||
hyperfine \
|
||||
--warmup 3 \
|
||||
--runs 20 \
|
||||
-i \
|
||||
--export-csv wota_vs_nota_vs_json.csv \
|
||||
--export-json wota_vs_nota_vs_json.json \
|
||||
--export-markdown wota_vs_nota_vs_json.md \
|
||||
--parameter-list lib wota,nota,json \
|
||||
--parameter-list scen empty,integers,floats,strings,objects,nested,large_array \
|
||||
'cell benchmarks/wota_nota_json {lib} {scen}'
|
||||
|
||||
|
||||
echo "Benchmark complete! Results saved to:"
|
||||
echo " - wota_vs_nota_vs_json.csv"
|
||||
echo " - wota_vs_nota_vs_json.json"
|
||||
echo " - wota_vs_nota_vs_json.md"
|
||||
@@ -1,395 +0,0 @@
|
||||
var time = use('time')
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// JavaScript Performance Benchmark Suite
|
||||
// Tests core JS operations: property access, function calls, arithmetic, etc.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Test configurations
|
||||
const iterations = {
|
||||
simple: 10000000,
|
||||
medium: 1000000,
|
||||
complex: 100000
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Utility: measureTime(fn) => how long fn() takes in seconds
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function measureTime(fn) {
|
||||
var start = time.number();
|
||||
fn();
|
||||
var end = time.number();
|
||||
return (end - start);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Benchmark: Property Access
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function benchPropertyAccess() {
|
||||
var obj = {
|
||||
a: 1, b: 2, c: 3, d: 4, e: 5,
|
||||
nested: { x: 10, y: 20, z: 30 }
|
||||
};
|
||||
|
||||
var readTime = measureTime(function() {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < iterations.simple; i++) {
|
||||
sum += obj.a + obj.b + obj.c + obj.d + obj.e;
|
||||
sum += obj.nested.x + obj.nested.y + obj.nested.z;
|
||||
}
|
||||
});
|
||||
|
||||
var writeTime = measureTime(function() {
|
||||
for (var i = 0; i < iterations.simple; i++) {
|
||||
obj.a = i;
|
||||
obj.b = i + 1;
|
||||
obj.c = i + 2;
|
||||
obj.nested.x = i * 2;
|
||||
obj.nested.y = i * 3;
|
||||
}
|
||||
});
|
||||
|
||||
return { readTime: readTime, writeTime: writeTime };
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Benchmark: Function Calls
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function benchFunctionCalls() {
|
||||
function add(a, b) { return a + b; }
|
||||
function multiply(a, b) { return a * b; }
|
||||
function complexCalc(a, b, c) { return (a + b) * c / 2; }
|
||||
|
||||
var obj = {
|
||||
method: function(x) { return x * 2; },
|
||||
nested: {
|
||||
deepMethod: function(x, y) { return x + y; }
|
||||
}
|
||||
};
|
||||
|
||||
var simpleCallTime = measureTime(function() {
|
||||
var result = 0;
|
||||
for (var i = 0; i < iterations.simple; i++) {
|
||||
result = add(i, 1);
|
||||
result = multiply(result, 2);
|
||||
}
|
||||
});
|
||||
|
||||
var methodCallTime = measureTime(function() {
|
||||
var result = 0;
|
||||
for (var i = 0; i < iterations.simple; i++) {
|
||||
result = obj.method(i);
|
||||
result = obj.nested.deepMethod(result, i);
|
||||
}
|
||||
});
|
||||
|
||||
var complexCallTime = measureTime(function() {
|
||||
var result = 0;
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
result = complexCalc(i, i + 1, i + 2);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
simpleCallTime: simpleCallTime,
|
||||
methodCallTime: methodCallTime,
|
||||
complexCallTime: complexCallTime
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Benchmark: Array Operations
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function benchArrayOps() {
|
||||
var pushTime = measureTime(function() {
|
||||
var arr = [];
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
arr.push(i);
|
||||
}
|
||||
});
|
||||
|
||||
var arr = [];
|
||||
for (var i = 0; i < 10000; i++) arr.push(i);
|
||||
|
||||
var accessTime = measureTime(function() {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
sum += arr[i % 10000];
|
||||
}
|
||||
});
|
||||
|
||||
var iterateTime = measureTime(function() {
|
||||
var sum = 0;
|
||||
for (var j = 0; j < 1000; j++) {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
sum += arr[i];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
pushTime: pushTime,
|
||||
accessTime: accessTime,
|
||||
iterateTime: iterateTime
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Benchmark: Object Creation
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function benchObjectCreation() {
|
||||
var literalTime = measureTime(function() {
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
var obj = { x: i, y: i * 2, z: i * 3 };
|
||||
}
|
||||
});
|
||||
|
||||
function Point(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
var constructorTime = measureTime(function() {
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
var p = new Point(i, i * 2);
|
||||
}
|
||||
});
|
||||
|
||||
var protoObj = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
move: function(dx, dy) {
|
||||
this.x += dx;
|
||||
this.y += dy;
|
||||
}
|
||||
};
|
||||
|
||||
var prototypeTime = measureTime(function() {
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
var obj = Object.create(protoObj);
|
||||
obj.x = i;
|
||||
obj.y = i * 2;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
literalTime: literalTime,
|
||||
constructorTime: constructorTime,
|
||||
prototypeTime: prototypeTime
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Benchmark: String Operations
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function benchStringOps() {
|
||||
var concatTime = measureTime(function() {
|
||||
var str = "";
|
||||
for (var i = 0; i < iterations.complex; i++) {
|
||||
str = "test" + i + "value";
|
||||
}
|
||||
});
|
||||
|
||||
var strings = [];
|
||||
for (var i = 0; i < 1000; i++) {
|
||||
strings.push("string" + i);
|
||||
}
|
||||
|
||||
var joinTime = measureTime(function() {
|
||||
for (var i = 0; i < iterations.complex; i++) {
|
||||
var result = strings.join(",");
|
||||
}
|
||||
});
|
||||
|
||||
var splitTime = measureTime(function() {
|
||||
var str = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p";
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
var parts = str.split(",");
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
concatTime: concatTime,
|
||||
joinTime: joinTime,
|
||||
splitTime: splitTime
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Benchmark: Arithmetic Operations
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function benchArithmetic() {
|
||||
var intMathTime = measureTime(function() {
|
||||
var result = 1;
|
||||
for (var i = 0; i < iterations.simple; i++) {
|
||||
result = ((result + i) * 2 - 1) / 3;
|
||||
result = result % 1000 + 1;
|
||||
}
|
||||
});
|
||||
|
||||
var floatMathTime = measureTime(function() {
|
||||
var result = 1.5;
|
||||
for (var i = 0; i < iterations.simple; i++) {
|
||||
result = Math.sin(result) + Math.cos(i * 0.01);
|
||||
result = Math.sqrt(Math.abs(result)) + 0.1;
|
||||
}
|
||||
});
|
||||
|
||||
var bitwiseTime = measureTime(function() {
|
||||
var result = 0;
|
||||
for (var i = 0; i < iterations.simple; i++) {
|
||||
result = (result ^ i) & 0xFFFF;
|
||||
result = (result << 1) | (result >> 15);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
intMathTime: intMathTime,
|
||||
floatMathTime: floatMathTime,
|
||||
bitwiseTime: bitwiseTime
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Benchmark: Closure Operations
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function benchClosures() {
|
||||
function makeAdder(x) {
|
||||
return function(y) { return x + y; };
|
||||
}
|
||||
|
||||
var closureCreateTime = measureTime(function() {
|
||||
var funcs = [];
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
funcs.push(makeAdder(i));
|
||||
}
|
||||
});
|
||||
|
||||
var adders = [];
|
||||
for (var i = 0; i < 1000; i++) {
|
||||
adders.push(makeAdder(i));
|
||||
}
|
||||
|
||||
var closureCallTime = measureTime(function() {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < iterations.medium; i++) {
|
||||
sum += adders[i % 1000](i);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
closureCreateTime: closureCreateTime,
|
||||
closureCallTime: closureCallTime
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Main benchmark runner
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
log.console("JavaScript Performance Benchmark");
|
||||
log.console("======================\n");
|
||||
|
||||
// Property Access
|
||||
log.console("BENCHMARK: Property Access");
|
||||
var propResults = benchPropertyAccess();
|
||||
log.console(" Read time: " + propResults.readTime.toFixed(3) + "s => " +
|
||||
(iterations.simple / propResults.readTime).toFixed(1) + " reads/sec [" +
|
||||
(propResults.readTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Write time: " + propResults.writeTime.toFixed(3) + "s => " +
|
||||
(iterations.simple / propResults.writeTime).toFixed(1) + " writes/sec [" +
|
||||
(propResults.writeTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console("");
|
||||
|
||||
// Function Calls
|
||||
log.console("BENCHMARK: Function Calls");
|
||||
var funcResults = benchFunctionCalls();
|
||||
log.console(" Simple calls: " + funcResults.simpleCallTime.toFixed(3) + "s => " +
|
||||
(iterations.simple / funcResults.simpleCallTime).toFixed(1) + " calls/sec [" +
|
||||
(funcResults.simpleCallTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Method calls: " + funcResults.methodCallTime.toFixed(3) + "s => " +
|
||||
(iterations.simple / funcResults.methodCallTime).toFixed(1) + " calls/sec [" +
|
||||
(funcResults.methodCallTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Complex calls: " + funcResults.complexCallTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / funcResults.complexCallTime).toFixed(1) + " calls/sec [" +
|
||||
(funcResults.complexCallTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console("");
|
||||
|
||||
// Array Operations
|
||||
log.console("BENCHMARK: Array Operations");
|
||||
var arrayResults = benchArrayOps();
|
||||
log.console(" Push: " + arrayResults.pushTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / arrayResults.pushTime).toFixed(1) + " pushes/sec [" +
|
||||
(arrayResults.pushTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Access: " + arrayResults.accessTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / arrayResults.accessTime).toFixed(1) + " accesses/sec [" +
|
||||
(arrayResults.accessTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Iterate: " + arrayResults.iterateTime.toFixed(3) + "s => " +
|
||||
(1000 / arrayResults.iterateTime).toFixed(1) + " full iterations/sec");
|
||||
log.console("");
|
||||
|
||||
// Object Creation
|
||||
log.console("BENCHMARK: Object Creation");
|
||||
var objResults = benchObjectCreation();
|
||||
log.console(" Literal: " + objResults.literalTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / objResults.literalTime).toFixed(1) + " creates/sec [" +
|
||||
(objResults.literalTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Constructor: " + objResults.constructorTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / objResults.constructorTime).toFixed(1) + " creates/sec [" +
|
||||
(objResults.constructorTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Prototype: " + objResults.prototypeTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / objResults.prototypeTime).toFixed(1) + " creates/sec [" +
|
||||
(objResults.prototypeTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console("");
|
||||
|
||||
// String Operations
|
||||
log.console("BENCHMARK: String Operations");
|
||||
var strResults = benchStringOps();
|
||||
log.console(" Concat: " + strResults.concatTime.toFixed(3) + "s => " +
|
||||
(iterations.complex / strResults.concatTime).toFixed(1) + " concats/sec [" +
|
||||
(strResults.concatTime / iterations.complex * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Join: " + strResults.joinTime.toFixed(3) + "s => " +
|
||||
(iterations.complex / strResults.joinTime).toFixed(1) + " joins/sec [" +
|
||||
(strResults.joinTime / iterations.complex * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Split: " + strResults.splitTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / strResults.splitTime).toFixed(1) + " splits/sec [" +
|
||||
(strResults.splitTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console("");
|
||||
|
||||
// Arithmetic Operations
|
||||
log.console("BENCHMARK: Arithmetic Operations");
|
||||
var mathResults = benchArithmetic();
|
||||
log.console(" Integer math: " + mathResults.intMathTime.toFixed(3) + "s => " +
|
||||
(iterations.simple / mathResults.intMathTime).toFixed(1) + " ops/sec [" +
|
||||
(mathResults.intMathTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Float math: " + mathResults.floatMathTime.toFixed(3) + "s => " +
|
||||
(iterations.simple / mathResults.floatMathTime).toFixed(1) + " ops/sec [" +
|
||||
(mathResults.floatMathTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Bitwise: " + mathResults.bitwiseTime.toFixed(3) + "s => " +
|
||||
(iterations.simple / mathResults.bitwiseTime).toFixed(1) + " ops/sec [" +
|
||||
(mathResults.bitwiseTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console("");
|
||||
|
||||
// Closures
|
||||
log.console("BENCHMARK: Closures");
|
||||
var closureResults = benchClosures();
|
||||
log.console(" Create: " + closureResults.closureCreateTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / closureResults.closureCreateTime).toFixed(1) + " creates/sec [" +
|
||||
(closureResults.closureCreateTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console(" Call: " + closureResults.closureCallTime.toFixed(3) + "s => " +
|
||||
(iterations.medium / closureResults.closureCallTime).toFixed(1) + " calls/sec [" +
|
||||
(closureResults.closureCallTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||
log.console("");
|
||||
|
||||
log.console("---------------------------------------------------------");
|
||||
log.console("Benchmark complete.\n");
|
||||
|
||||
$_.stop()
|
||||
@@ -1,40 +0,0 @@
|
||||
var blob = use('blob')
|
||||
|
||||
var iter = 50, limit = 2.0;
|
||||
var zr, zi, cr, ci, tr, ti;
|
||||
|
||||
var h = Number(arg[0]) || 500
|
||||
var w = h
|
||||
|
||||
log.console(`P4\n${w} ${h}`);
|
||||
|
||||
for (let y = 0; y < h; ++y) {
|
||||
// Create a blob for the row - we need w bits
|
||||
var row = new blob(w);
|
||||
|
||||
for (let x = 0; x < w; ++x) {
|
||||
zr = zi = tr = ti = 0;
|
||||
cr = 2 * x / w - 1.5;
|
||||
ci = 2 * y / h - 1;
|
||||
for (let i = 0; i < iter && (tr + ti <= limit * limit); ++i) {
|
||||
zi = 2 * zr * zi + ci;
|
||||
zr = tr - ti + cr;
|
||||
tr = zr * zr;
|
||||
ti = zi * zi;
|
||||
}
|
||||
|
||||
// Write a 1 bit if inside the set, 0 if outside
|
||||
if (tr + ti <= limit * limit)
|
||||
row.write_bit(1);
|
||||
else
|
||||
row.write_bit(0);
|
||||
}
|
||||
|
||||
// Convert the blob to stone (immutable) to prepare for output
|
||||
stone(row)
|
||||
|
||||
// Output the blob data as raw bytes
|
||||
log.console(text(row, 'b'));
|
||||
}
|
||||
|
||||
$_.stop()
|
||||
@@ -1,11 +0,0 @@
|
||||
var N = 1000000;
|
||||
var num = 0;
|
||||
for (var i = 0; i < N; i ++) {
|
||||
var x = 2 * $_.random();
|
||||
var y = $_.random();
|
||||
if (y < Math.sin(x * x))
|
||||
num++;
|
||||
}
|
||||
log.console(2 * num / N);
|
||||
|
||||
$_.stop()
|
||||
@@ -1,161 +0,0 @@
|
||||
var PI = Math.PI;
|
||||
var SOLAR_MASS = 4 * PI * PI;
|
||||
var DAYS_PER_YEAR = 365.24;
|
||||
|
||||
function Body(x, y, z, vx, vy, vz, mass) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.vx = vx;
|
||||
this.vy = vy;
|
||||
this.vz = vz;
|
||||
this.mass = mass;
|
||||
}
|
||||
|
||||
function Jupiter() {
|
||||
return new Body(
|
||||
4.84143144246472090e+00,
|
||||
-1.16032004402742839e+00,
|
||||
-1.03622044471123109e-01,
|
||||
1.66007664274403694e-03 * DAYS_PER_YEAR,
|
||||
7.69901118419740425e-03 * DAYS_PER_YEAR,
|
||||
-6.90460016972063023e-05 * DAYS_PER_YEAR,
|
||||
9.54791938424326609e-04 * SOLAR_MASS
|
||||
);
|
||||
}
|
||||
|
||||
function Saturn() {
|
||||
return new Body(
|
||||
8.34336671824457987e+00,
|
||||
4.12479856412430479e+00,
|
||||
-4.03523417114321381e-01,
|
||||
-2.76742510726862411e-03 * DAYS_PER_YEAR,
|
||||
4.99852801234917238e-03 * DAYS_PER_YEAR,
|
||||
2.30417297573763929e-05 * DAYS_PER_YEAR,
|
||||
2.85885980666130812e-04 * SOLAR_MASS
|
||||
);
|
||||
}
|
||||
|
||||
function Uranus() {
|
||||
return new Body(
|
||||
1.28943695621391310e+01,
|
||||
-1.51111514016986312e+01,
|
||||
-2.23307578892655734e-01,
|
||||
2.96460137564761618e-03 * DAYS_PER_YEAR,
|
||||
2.37847173959480950e-03 * DAYS_PER_YEAR,
|
||||
-2.96589568540237556e-05 * DAYS_PER_YEAR,
|
||||
4.36624404335156298e-05 * SOLAR_MASS
|
||||
);
|
||||
}
|
||||
|
||||
function Neptune() {
|
||||
return new Body(
|
||||
1.53796971148509165e+01,
|
||||
-2.59193146099879641e+01,
|
||||
1.79258772950371181e-01,
|
||||
2.68067772490389322e-03 * DAYS_PER_YEAR,
|
||||
1.62824170038242295e-03 * DAYS_PER_YEAR,
|
||||
-9.51592254519715870e-05 * DAYS_PER_YEAR,
|
||||
5.15138902046611451e-05 * SOLAR_MASS
|
||||
);
|
||||
}
|
||||
|
||||
function Sun() {
|
||||
return new Body(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, SOLAR_MASS);
|
||||
}
|
||||
|
||||
var bodies = Array(Sun(), Jupiter(), Saturn(), Uranus(), Neptune());
|
||||
|
||||
function offsetMomentum() {
|
||||
let px = 0;
|
||||
let py = 0;
|
||||
let pz = 0;
|
||||
var size = bodies.length;
|
||||
for (let i = 0; i < size; i++) {
|
||||
var body = bodies[i];
|
||||
var mass = body.mass;
|
||||
px += body.vx * mass;
|
||||
py += body.vy * mass;
|
||||
pz += body.vz * mass;
|
||||
}
|
||||
|
||||
var body = bodies[0];
|
||||
body.vx = -px / SOLAR_MASS;
|
||||
body.vy = -py / SOLAR_MASS;
|
||||
body.vz = -pz / SOLAR_MASS;
|
||||
}
|
||||
|
||||
function advance(dt) {
|
||||
var size = bodies.length;
|
||||
|
||||
for (let i = 0; i < size; i++) {
|
||||
var bodyi = bodies[i];
|
||||
let vxi = bodyi.vx;
|
||||
let vyi = bodyi.vy;
|
||||
let vzi = bodyi.vz;
|
||||
for (let j = i + 1; j < size; j++) {
|
||||
var bodyj = bodies[j];
|
||||
var dx = bodyi.x - bodyj.x;
|
||||
var dy = bodyi.y - bodyj.y;
|
||||
var dz = bodyi.z - bodyj.z;
|
||||
|
||||
var d2 = dx * dx + dy * dy + dz * dz;
|
||||
var mag = dt / (d2 * Math.sqrt(d2));
|
||||
|
||||
var massj = bodyj.mass;
|
||||
vxi -= dx * massj * mag;
|
||||
vyi -= dy * massj * mag;
|
||||
vzi -= dz * massj * mag;
|
||||
|
||||
var massi = bodyi.mass;
|
||||
bodyj.vx += dx * massi * mag;
|
||||
bodyj.vy += dy * massi * mag;
|
||||
bodyj.vz += dz * massi * mag;
|
||||
}
|
||||
bodyi.vx = vxi;
|
||||
bodyi.vy = vyi;
|
||||
bodyi.vz = vzi;
|
||||
}
|
||||
|
||||
for (let i = 0; i < size; i++) {
|
||||
var body = bodies[i];
|
||||
body.x += dt * body.vx;
|
||||
body.y += dt * body.vy;
|
||||
body.z += dt * body.vz;
|
||||
}
|
||||
}
|
||||
|
||||
function energy() {
|
||||
let e = 0;
|
||||
var size = bodies.length;
|
||||
|
||||
for (let i = 0; i < size; i++) {
|
||||
var bodyi = bodies[i];
|
||||
|
||||
e += 0.5 * bodyi.mass * ( bodyi.vx * bodyi.vx +
|
||||
bodyi.vy * bodyi.vy + bodyi.vz * bodyi.vz );
|
||||
|
||||
for (let j = i + 1; j < size; j++) {
|
||||
var bodyj = bodies[j];
|
||||
var dx = bodyi.x - bodyj.x;
|
||||
var dy = bodyi.y - bodyj.y;
|
||||
var dz = bodyi.z - bodyj.z;
|
||||
|
||||
var distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
e -= (bodyi.mass * bodyj.mass) / distance;
|
||||
}
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
var n = arg[0] || 1000000
|
||||
|
||||
offsetMomentum();
|
||||
|
||||
log.console(`n = ${n}`)
|
||||
log.console(energy().toFixed(9))
|
||||
for (let i = 0; i < n; i++)
|
||||
advance(0.01);
|
||||
log.console(energy().toFixed(9))
|
||||
|
||||
$_.stop()
|
||||
@@ -1,76 +0,0 @@
|
||||
var nota = use('nota')
|
||||
var os = use('os')
|
||||
var io = use('io')
|
||||
|
||||
var ll = io.slurp('benchmarks/nota.json')
|
||||
|
||||
var newarr = []
|
||||
var accstr = ""
|
||||
for (var i = 0; i < 10000; i++) {
|
||||
accstr += i;
|
||||
newarr.push(i.toString())
|
||||
}
|
||||
// Arrays to store timing results
|
||||
var jsonDecodeTimes = [];
|
||||
var jsonEncodeTimes = [];
|
||||
var notaEncodeTimes = [];
|
||||
var notaDecodeTimes = [];
|
||||
var notaSizes = [];
|
||||
|
||||
// Run 100 tests
|
||||
for (let i = 0; i < 100; i++) {
|
||||
// JSON Decode test
|
||||
let start = os.now();
|
||||
var jll = json.decode(ll);
|
||||
jsonDecodeTimes.push((os.now() - start) * 1000);
|
||||
|
||||
// JSON Encode test
|
||||
start = os.now();
|
||||
let jsonStr = JSON.stringify(jll);
|
||||
jsonEncodeTimes.push((os.now() - start) * 1000);
|
||||
|
||||
// NOTA Encode test
|
||||
start = os.now();
|
||||
var nll = nota.encode(jll);
|
||||
notaEncodeTimes.push((os.now() - start) * 1000);
|
||||
|
||||
// NOTA Decode test
|
||||
start = os.now();
|
||||
var oll = nota.decode(nll);
|
||||
notaDecodeTimes.push((os.now() - start) * 1000);
|
||||
}
|
||||
|
||||
// Calculate statistics
|
||||
function getStats(arr) {
|
||||
def avg = arr.reduce((a, b) => a + b) / arr.length;
|
||||
def min = Math.min(...arr);
|
||||
def max = Math.max(...arr);
|
||||
return { avg, min, max };
|
||||
}
|
||||
|
||||
// Pretty print results
|
||||
log.console("\n== Performance Test Results (100 iterations) ==");
|
||||
log.console("\nJSON Decoding (ms):");
|
||||
def jsonDecStats = getStats(jsonDecodeTimes);
|
||||
log.console(`Average: ${jsonDecStats.avg.toFixed(2)} ms`);
|
||||
log.console(`Min: ${jsonDecStats.min.toFixed(2)} ms`);
|
||||
log.console(`Max: ${jsonDecStats.max.toFixed(2)} ms`);
|
||||
|
||||
log.console("\nJSON Encoding (ms):");
|
||||
def jsonEncStats = getStats(jsonEncodeTimes);
|
||||
log.console(`Average: ${jsonEncStats.avg.toFixed(2)} ms`);
|
||||
log.console(`Min: ${jsonEncStats.min.toFixed(2)} ms`);
|
||||
log.console(`Max: ${jsonEncStats.max.toFixed(2)} ms`);
|
||||
|
||||
log.console("\nNOTA Encoding (ms):");
|
||||
def notaEncStats = getStats(notaEncodeTimes);
|
||||
log.console(`Average: ${notaEncStats.avg.toFixed(2)} ms`);
|
||||
log.console(`Min: ${notaEncStats.min.toFixed(2)} ms`);
|
||||
log.console(`Max: ${notaEncStats.max.toFixed(2)} ms`);
|
||||
|
||||
log.console("\nNOTA Decoding (ms):");
|
||||
def notaDecStats = getStats(notaDecodeTimes);
|
||||
log.console(`Average: ${notaDecStats.avg.toFixed(2)} ms`);
|
||||
log.console(`Min: ${notaDecStats.min.toFixed(2)} ms`);
|
||||
log.console(`Max: ${notaDecStats.max.toFixed(2)} ms`);
|
||||
|
||||
76
benchmarks/nota.js
Normal file
@@ -0,0 +1,76 @@
|
||||
var nota = use('nota')
|
||||
var os = use('os')
|
||||
var io = use('io')
|
||||
|
||||
var ll = io.slurp('benchmarks/nota.json')
|
||||
|
||||
var newarr = []
|
||||
var accstr = ""
|
||||
for (var i = 0; i < 10000; i++) {
|
||||
accstr += i;
|
||||
newarr.push(i.toString())
|
||||
}
|
||||
// Arrays to store timing results
|
||||
var jsonDecodeTimes = [];
|
||||
var jsonEncodeTimes = [];
|
||||
var notaEncodeTimes = [];
|
||||
var notaDecodeTimes = [];
|
||||
var notaSizes = [];
|
||||
|
||||
// Run 100 tests
|
||||
for (let i = 0; i < 100; i++) {
|
||||
// JSON Decode test
|
||||
let start = os.now();
|
||||
var jll = json.decode(ll);
|
||||
jsonDecodeTimes.push((os.now() - start) * 1000);
|
||||
|
||||
// JSON Encode test
|
||||
start = os.now();
|
||||
let jsonStr = JSON.stringify(jll);
|
||||
jsonEncodeTimes.push((os.now() - start) * 1000);
|
||||
|
||||
// NOTA Encode test
|
||||
start = os.now();
|
||||
var nll = nota.encode(jll);
|
||||
notaEncodeTimes.push((os.now() - start) * 1000);
|
||||
|
||||
// NOTA Decode test
|
||||
start = os.now();
|
||||
var oll = nota.decode(nll);
|
||||
notaDecodeTimes.push((os.now() - start) * 1000);
|
||||
}
|
||||
|
||||
// Calculate statistics
|
||||
function getStats(arr) {
|
||||
const avg = arr.reduce((a, b) => a + b) / arr.length;
|
||||
const min = Math.min(...arr);
|
||||
const max = Math.max(...arr);
|
||||
return { avg, min, max };
|
||||
}
|
||||
|
||||
// Pretty print results
|
||||
console.log("\n=== Performance Test Results (100 iterations) ===");
|
||||
console.log("\nJSON Decoding (ms):");
|
||||
const jsonDecStats = getStats(jsonDecodeTimes);
|
||||
console.log(`Average: ${jsonDecStats.avg.toFixed(2)} ms`);
|
||||
console.log(`Min: ${jsonDecStats.min.toFixed(2)} ms`);
|
||||
console.log(`Max: ${jsonDecStats.max.toFixed(2)} ms`);
|
||||
|
||||
console.log("\nJSON Encoding (ms):");
|
||||
const jsonEncStats = getStats(jsonEncodeTimes);
|
||||
console.log(`Average: ${jsonEncStats.avg.toFixed(2)} ms`);
|
||||
console.log(`Min: ${jsonEncStats.min.toFixed(2)} ms`);
|
||||
console.log(`Max: ${jsonEncStats.max.toFixed(2)} ms`);
|
||||
|
||||
console.log("\nNOTA Encoding (ms):");
|
||||
const notaEncStats = getStats(notaEncodeTimes);
|
||||
console.log(`Average: ${notaEncStats.avg.toFixed(2)} ms`);
|
||||
console.log(`Min: ${notaEncStats.min.toFixed(2)} ms`);
|
||||
console.log(`Max: ${notaEncStats.max.toFixed(2)} ms`);
|
||||
|
||||
console.log("\nNOTA Decoding (ms):");
|
||||
const notaDecStats = getStats(notaDecodeTimes);
|
||||
console.log(`Average: ${notaDecStats.avg.toFixed(2)} ms`);
|
||||
console.log(`Min: ${notaDecStats.min.toFixed(2)} ms`);
|
||||
console.log(`Max: ${notaDecStats.max.toFixed(2)} ms`);
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
function A(i,j) {
|
||||
return 1/((i+j)*(i+j+1)/2+i+1);
|
||||
}
|
||||
|
||||
function Au(u,v) {
|
||||
for (var i=0; i<u.length; ++i) {
|
||||
var t = 0;
|
||||
for (var j=0; j<u.length; ++j)
|
||||
t += A(i,j) * u[j];
|
||||
|
||||
v[i] = t;
|
||||
}
|
||||
}
|
||||
|
||||
function Atu(u,v) {
|
||||
for (var i=0; i<u.length; ++i) {
|
||||
var t = 0;
|
||||
for (var j=0; j<u.length; ++j)
|
||||
t += A(j,i) * u[j];
|
||||
|
||||
v[i] = t;
|
||||
}
|
||||
}
|
||||
|
||||
function AtAu(u,v,w) {
|
||||
Au(u,w);
|
||||
Atu(w,v);
|
||||
}
|
||||
|
||||
function spectralnorm(n) {
|
||||
var i, u=[], v=[], w=[], vv=0, vBv=0;
|
||||
for (i=0; i<n; ++i)
|
||||
u[i] = 1; v[i] = w[i] = 0;
|
||||
|
||||
for (i=0; i<10; ++i) {
|
||||
AtAu(u,v,w);
|
||||
AtAu(v,u,w);
|
||||
}
|
||||
|
||||
for (i=0; i<n; ++i) {
|
||||
vBv += u[i]*v[i];
|
||||
vv += v[i]*v[i];
|
||||
}
|
||||
|
||||
return Math.sqrt(vBv/vv);
|
||||
}
|
||||
|
||||
log.console(spectralnorm(arg[0]).toFixed(9));
|
||||
|
||||
$_.stop()
|
||||
@@ -36,7 +36,7 @@ function roundTripWota(value) {
|
||||
// iterations: how many times to loop
|
||||
//
|
||||
// You can tweak these as you like for heavier or lighter tests.
|
||||
def benchmarks = [
|
||||
const benchmarks = [
|
||||
{
|
||||
name: "Small Integers",
|
||||
data: [0, 42, -1, 2023],
|
||||
@@ -75,8 +75,8 @@ def benchmarks = [
|
||||
];
|
||||
|
||||
// Print a header
|
||||
log.console("Wota Encode/Decode Benchmark");
|
||||
log.console("===================\n");
|
||||
console.log("Wota Encode/Decode Benchmark");
|
||||
console.log("============================\n");
|
||||
|
||||
// We'll run each benchmark scenario in turn.
|
||||
for (let bench of benchmarks) {
|
||||
@@ -96,11 +96,11 @@ for (let bench of benchmarks) {
|
||||
let elapsedSec = measureTime(runAllData, bench.iterations);
|
||||
let opsPerSec = (totalIterations / elapsedSec).toFixed(1);
|
||||
|
||||
log.console(`${bench.name}:`);
|
||||
log.console(` Iterations: ${bench.iterations} × ${bench.data.length} data items = ${totalIterations}`);
|
||||
log.console(` Elapsed: ${elapsedSec.toFixed(3)} s`);
|
||||
log.console(` Throughput: ${opsPerSec} encode+decode ops/sec\n`);
|
||||
console.log(`${bench.name}:`);
|
||||
console.log(` Iterations: ${bench.iterations} × ${bench.data.length} data items = ${totalIterations}`);
|
||||
console.log(` Elapsed: ${elapsedSec.toFixed(3)} s`);
|
||||
console.log(` Throughput: ${opsPerSec} encode+decode ops/sec\n`);
|
||||
}
|
||||
|
||||
// All done
|
||||
log.console("Benchmark completed.\n");
|
||||
console.log("Benchmark completed.\n");
|
||||
@@ -2,53 +2,43 @@
|
||||
// benchmark_wota_nota_json.js
|
||||
//
|
||||
// Usage in QuickJS:
|
||||
// qjs benchmark_wota_nota_json.js <LibraryName> <ScenarioName>
|
||||
// qjs benchmark_wota_nota_json.js
|
||||
//
|
||||
// Ensure wota, nota, json, and os are all available, e.g.:
|
||||
var wota = use('wota');
|
||||
var nota = use('nota');
|
||||
var json = use('json');
|
||||
var jswota = use('jswota')
|
||||
var os = use('os');
|
||||
//
|
||||
|
||||
// Parse command line arguments
|
||||
if (arg.length != 2) {
|
||||
log.console('Usage: cell benchmark_wota_nota_json.ce <LibraryName> <ScenarioName>');
|
||||
$_.stop()
|
||||
}
|
||||
|
||||
var lib_name = arg[0];
|
||||
var scenario_name = arg[1];
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 1. Setup "libraries" array to easily switch among wota, nota, and json
|
||||
// 1. Setup "libraries" array to easily switch among Wota, Nota, and JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
def libraries = [
|
||||
const libraries = [
|
||||
{
|
||||
name: "wota",
|
||||
name: "Wota",
|
||||
encode: wota.encode,
|
||||
decode: wota.decode,
|
||||
// wota produces an ArrayBuffer. We'll count `buffer.byteLength` as size.
|
||||
// Wota produces an ArrayBuffer. We'll count `buffer.byteLength` as size.
|
||||
getSize(encoded) {
|
||||
return encoded.length;
|
||||
return encoded.byteLength;
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "nota",
|
||||
name: "Nota",
|
||||
encode: nota.encode,
|
||||
decode: nota.decode,
|
||||
// nota also produces an ArrayBuffer:
|
||||
// Nota also produces an ArrayBuffer:
|
||||
getSize(encoded) {
|
||||
return encoded.length;
|
||||
return encoded.byteLength;
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "json",
|
||||
name: "JSON",
|
||||
encode: json.encode,
|
||||
decode: json.decode,
|
||||
// json produces a JS string. We'll measure its UTF-16 code unit length
|
||||
// JSON produces a JS string. We'll measure its UTF-16 code unit length
|
||||
// as a rough "size". Alternatively, you could convert to UTF-8 for
|
||||
// a more accurate byte size. Here we just use `string.length`.
|
||||
getSize(encodedStr) {
|
||||
@@ -62,29 +52,24 @@ def libraries = [
|
||||
// Each scenario has { name, data, iterations }
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
def benchmarks = [
|
||||
const benchmarks = [
|
||||
{
|
||||
name: "empty",
|
||||
data: [{}, {}, {}, {}],
|
||||
iterations: 10000
|
||||
},
|
||||
{
|
||||
name: "integers",
|
||||
name: "Small Integers",
|
||||
data: [0, 42, -1, 2023],
|
||||
iterations: 100000
|
||||
},
|
||||
{
|
||||
name: "floats",
|
||||
name: "Floating point",
|
||||
data: [0.1, 1e-50, 3.14159265359],
|
||||
iterations: 100000
|
||||
},
|
||||
{
|
||||
name: "strings",
|
||||
data: ["Hello, wota!", "short", "Emoji: \u{1f600}\u{1f64f}"],
|
||||
name: "Strings (short, emoji)",
|
||||
data: ["Hello, Wota!", "short", "Emoji: \u{1f600}\u{1f64f}"],
|
||||
iterations: 100000
|
||||
},
|
||||
{
|
||||
name: "objects",
|
||||
name: "Small Objects",
|
||||
data: [
|
||||
{ a:1, b:2.2, c:"3", d:false },
|
||||
{ x:42, y:null, z:"test" }
|
||||
@@ -92,15 +77,20 @@ def benchmarks = [
|
||||
iterations: 50000
|
||||
},
|
||||
{
|
||||
name: "nested",
|
||||
name: "Nested Arrays",
|
||||
data: [ [ [ [1,2], [3,4] ] ], [[[]]], [1, [2, [3, [4]]]] ],
|
||||
iterations: 50000
|
||||
},
|
||||
{
|
||||
name: "large_array",
|
||||
name: "Large Array (1k integers)",
|
||||
data: [ Array.from({length:1000}, (_, i) => i) ],
|
||||
iterations: 1000
|
||||
},
|
||||
{
|
||||
name: "Large Binary Blob (256KB)",
|
||||
data: [ new Uint8Array(256 * 1024).buffer ],
|
||||
iterations: 200
|
||||
}
|
||||
];
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -118,7 +108,7 @@ function measureTime(fn) {
|
||||
// 4. For each library, we run each benchmark scenario and measure:
|
||||
// - Encoding time (seconds)
|
||||
// - Decoding time (seconds)
|
||||
// - Total encoded size (bytes or code units for json)
|
||||
// - Total encoded size (bytes or code units for JSON)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -135,11 +125,11 @@ function runBenchmarkForLibrary(lib, bench) {
|
||||
let encodeTime = measureTime(() => {
|
||||
for (let i = 0; i < bench.iterations; i++) {
|
||||
// For each data item, encode it
|
||||
for (let j = 0; j < bench.data.length; j++) {
|
||||
let e = lib.encode(bench.data[j]);
|
||||
for (let d of bench.data) {
|
||||
let e = lib.encode(d);
|
||||
// store only in the very first iteration, so we can decode them later
|
||||
// but do not store them every iteration or we blow up memory.
|
||||
if (i == 0) {
|
||||
if (i === 0) {
|
||||
encodedList.push(e);
|
||||
totalSize += lib.getSize(e);
|
||||
}
|
||||
@@ -162,43 +152,31 @@ function runBenchmarkForLibrary(lib, bench) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 5. Main driver: run only the specified library and scenario
|
||||
// 5. Main driver: run across all benchmarks, for each library.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Find the requested library and scenario
|
||||
var lib = libraries.find(l => l.name == lib_name);
|
||||
var bench = benchmarks.find(b => b.name == scenario_name);
|
||||
console.log("Benchmark: Wota vs Nota vs JSON");
|
||||
console.log("================================\n");
|
||||
|
||||
if (!lib) {
|
||||
log.console('Unknown library:', lib_name);
|
||||
log.console('Available libraries:', libraries.map(l => l.name).join(', '));
|
||||
$_.stop()
|
||||
for (let bench of benchmarks) {
|
||||
console.log(`SCENARIO: ${bench.name}`);
|
||||
console.log(` Data length: ${bench.data.length} | Iterations: ${bench.iterations}\n`);
|
||||
|
||||
for (let lib of libraries) {
|
||||
let { encodeTime, decodeTime, totalSize } = runBenchmarkForLibrary(lib, bench);
|
||||
|
||||
// We'll compute total operations = bench.iterations * bench.data.length
|
||||
let totalOps = bench.iterations * bench.data.length;
|
||||
let encOpsPerSec = (totalOps / encodeTime).toFixed(1);
|
||||
let decOpsPerSec = (totalOps / decodeTime).toFixed(1);
|
||||
|
||||
console.log(` ${lib.name}:`);
|
||||
console.log(` Encode time: ${encodeTime.toFixed(3)}s => ${encOpsPerSec} encodes/sec`);
|
||||
console.log(` Decode time: ${decodeTime.toFixed(3)}s => ${decOpsPerSec} decodes/sec`);
|
||||
console.log(` Total size: ${totalSize} bytes (or code units for JSON)`);
|
||||
console.log("");
|
||||
}
|
||||
console.log("---------------------------------------------------------\n");
|
||||
}
|
||||
|
||||
if (!bench) {
|
||||
log.console('Unknown scenario:', scenario_name);
|
||||
log.console('Available scenarios:', benchmarks.map(b => b.name).join(', '));
|
||||
$_.stop()
|
||||
}
|
||||
|
||||
// Run the benchmark for this library/scenario combination
|
||||
var { encodeTime, decodeTime, totalSize } = runBenchmarkForLibrary(lib, bench);
|
||||
|
||||
// Output json for easy parsing by hyperfine or other tools
|
||||
var totalOps = bench.iterations * bench.data.length;
|
||||
var result = {
|
||||
lib: lib_name,
|
||||
scenario: scenario_name,
|
||||
encodeTime: encodeTime,
|
||||
decodeTime: decodeTime,
|
||||
totalSize: totalSize,
|
||||
totalOps: totalOps,
|
||||
encodeOpsPerSec: totalOps / encodeTime,
|
||||
decodeOpsPerSec: totalOps / decodeTime,
|
||||
encodeNsPerOp: (encodeTime / totalOps) * 1e9,
|
||||
decodeNsPerOp: (decodeTime / totalOps) * 1e9
|
||||
};
|
||||
|
||||
log.console(json.encode(result));
|
||||
|
||||
$_.stop()
|
||||
console.log("Benchmark complete.\n");
|
||||
122
cell.md
@@ -1,122 +0,0 @@
|
||||
JAVASCRIPT VISION
|
||||
|
||||
CELLSCRIPT
|
||||
|
||||
Javascript to its core. Objects. What does the language need? It can be quite small, I think. The key is, ANYTHING that we want to be fast and JIT'd, must be present. So, record lookups. These are actually quicker in a jit'd language that have them as a feature. Most things should be libraries. Blobs need to be in the runtime.
|
||||
|
||||
The purpose of this is to be a great language for passing messages. So it should be fast at creating records first and foremost, and finding items on them. So it needs first class, jitt'd records.
|
||||
|
||||
Finally, it needs to use less memory. Deleting a bunch of this stuff should make that simpler.
|
||||
|
||||
What is present?
|
||||
Objects, prototypes, numbers, arrays, strings, true, false, null.
|
||||
|
||||
Things to do:
|
||||
|
||||
merge typeof and instanceof. Misty has array? stone? number? etc; it needs to be generic. 5 is number returns true.
|
||||
|
||||
No new operator. It's the same idea though: simply instead of 'var guy = new sprite({x,y})' you would say 'var guy = sprite({x,y})', and sprite would simply be a function written to return a sprite object.
|
||||
|
||||
One number type. Dec64. Numeric stack can be added in later: a bigint library, for example, built inside cell.
|
||||
|
||||
Simplify the property attributes stuff. It is simple: objects have text keys and whatever values. Objects can also have objects as values. These work like symbols. You can share them, if desired. No well known symbols exist to eliminate that much misdirection. Obejcts basically work like private keys. If you serialize an object, objects that are keys are not serialized; only textual keys are. You can do something about it with a json() method that is invoked, if you desire. You cannot retrieve
|
||||
|
||||
var works like let; use var instead of let
|
||||
|
||||
no const
|
||||
|
||||
Function closures and _ => all work the same and close over the 'this' variable
|
||||
|
||||
Totally delete modules, coroutines, generators, proxy .. this deletes a lot of the big switch statement
|
||||
|
||||
Add the 'go' statement for tail calls
|
||||
|
||||
Add the 'do' statement
|
||||
|
||||
Implementation detail: separate out arrays and objects. They are not the same. Objects no longer need to track if they're fast arrays or not. They're not. Arrays are. Always.
|
||||
|
||||
Add the functional proxy idea. Log will be implemented through that.
|
||||
|
||||
Remove ===; it's just == now, and !=.
|
||||
|
||||
Remove 'continue'; now, break handles both. For a do statement, label it, and break to that label; so
|
||||
|
||||
var x = 0
|
||||
do loop {
|
||||
x++
|
||||
if (x < 5) break loop // goes back to loop
|
||||
break // exits loop
|
||||
}
|
||||
|
||||
rename instanceof to 'is'
|
||||
|
||||
remove undefined; all are 'null' now
|
||||
|
||||
remove 'delete'; to remove a field, assign it to null
|
||||
|
||||
remove with
|
||||
|
||||
Remove Object. New records have a prototype of nothing. There are no more 'type prototypes' at all.
|
||||
|
||||
Arrays are their own type
|
||||
|
||||
Remove property descriptors. Properties are always settable, unless the object as a whole is stone. Stone is an object property instead of a shape property.
|
||||
|
||||
Syntax stuff .. would like to invoke functions without (). This can effectively simulate a "getter". Make ? and all other characters usable for names. No reserve words, which are endlessly irritating.
|
||||
|
||||
----
|
||||
|
||||
This will all actually come about gradually. Add a few things at a time, fix up code that did not adhere. For a lot of this, no new functions will even need to be written; it's a matter of not calling certain functions that are no longer relevant, or calling different functions when required.
|
||||
|
||||
|
||||
## Benchmarks to implement
|
||||
### general speed
|
||||
binarytrees
|
||||
coro-prime-sieve
|
||||
edigits
|
||||
fannkuch-redux
|
||||
fasta
|
||||
http-server
|
||||
json serialize/deserialize
|
||||
knucleotide
|
||||
lru
|
||||
mandelbrot
|
||||
merkletrees
|
||||
nbody
|
||||
nsieve
|
||||
pidigits
|
||||
regex-redux
|
||||
secp256k1
|
||||
spectral-norm
|
||||
|
||||
### function calling and recursion stress - test goto
|
||||
naive recursive fibonacci [fib(35) or fib(40)]
|
||||
tak
|
||||
ackermann
|
||||
|
||||
### numeric
|
||||
sieve of eratosthenes [10^7 bits]
|
||||
spectral norm [5500 x 5500 matrix]
|
||||
n-body sim [50 000 - 100 000 steps]
|
||||
mandelbrot [1600x1200 image, max iter = 50]
|
||||
|
||||
### memory & gc torture
|
||||
binary trees [depth 18 (~500 000 nodes)]
|
||||
richards task scheduler
|
||||
fannkuch redux [n=11 or 12]
|
||||
|
||||
### dynamic object & property access
|
||||
deltablue constraint solver
|
||||
splay tree [256k nodes]
|
||||
json, wota, nota decode->encode [use 2MB example]
|
||||
|
||||
### string / regex kernels
|
||||
regex-DNA
|
||||
fasta
|
||||
word-frequency
|
||||
|
||||
### concurrency/message passing
|
||||
ping-pong [two actors exhange a small record N times, 1M messages end to end]
|
||||
chameneos [mating color swap game w/ randezvous]
|
||||
|
||||
For all, track memory and time.
|
||||
@@ -47,7 +47,7 @@ Certain functions are intrinsic to the program and cannot be overridden. They’
|
||||
- **Example**:
|
||||
```js
|
||||
this.delay(_ => {
|
||||
log.console("3 seconds later!")
|
||||
console.log("3 seconds later!")
|
||||
}, 3)
|
||||
```
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
|
||||
Provides a consistent way to create documentation for prosperon elements. Objects are documented by adding docstrings directly to object-like things (functions, objects, ...), or to an object's own "doc object".
|
||||
|
||||
Docstrings are set to the symbol `cell.DOC`
|
||||
Docstrings are set to the symbol `prosperon.DOC`
|
||||
|
||||
```js
|
||||
// Suppose we have a module that returns a function
|
||||
function greet(name) { log.console("Hello, " + name) }
|
||||
function greet(name) { console.log("Hello, " + name) }
|
||||
|
||||
// We can attach a docstring
|
||||
greet.doc = `
|
||||
@@ -21,12 +21,12 @@ return greet
|
||||
```js
|
||||
// Another way is to add a docstring object to an object
|
||||
var greet = {
|
||||
hello() { log.console('hello!') }
|
||||
hello() { console.log('hello!') }
|
||||
}
|
||||
|
||||
greet[cell.DOC] = {}
|
||||
greet[cell.DOC][cell.DOC] = 'An object full of different greeter functions'
|
||||
greet[cell.DOC].hello = 'A greeter that says, "hello!"'
|
||||
greet[prosperon.DOC] = {}
|
||||
greet[prosperon.DOC][prosperon.DOC] = 'An object full of different greeter functions'
|
||||
greet[prosperon.DOC].hello = 'A greeter that says, "hello!"'
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ return {
|
||||
This will cause prosperon to launch a 500x500 window with the title 'Hello World'. In your ```main.js```, write the following:
|
||||
|
||||
```
|
||||
log.console("Hello world")
|
||||
console.log("Hello world")
|
||||
|
||||
this.delay(_ => {
|
||||
this.kill();
|
||||
@@ -62,6 +62,6 @@ this.delay(_ => {
|
||||
The global object called `prosperon` has a variety of engine specific settings on it that can be set to influence how the engine behaves. For example, `prosperon.argv` contains a list of the command line arguments given to prosperon; `prosperon.PATH` is an array of paths to resolve resources such as modules and images. `prosperon` is fully documented in the API section.
|
||||
|
||||
## Getting help
|
||||
The `prosperon` global has a 'doc' function, which can be invoked on any engine object to see a description of it and its members. For example, to learn about `prosperon`, try printing out `cell.DOC(prosperon)` in your `main.js`.
|
||||
The `prosperon` global has a 'doc' function, which can be invoked on any engine object to see a description of it and its members. For example, to learn about `prosperon`, try printing out `prosperon.doc(prosperon)` in your `main.js`.
|
||||
|
||||
Writing documentation for your own modules and game components will be explored in the chapter on actors & modules.
|
||||
|
Before Width: | Height: | Size: 449 B After Width: | Height: | Size: 449 B |
@@ -5,7 +5,6 @@ var sprite = use('sprite')
|
||||
var geom = use('geometry')
|
||||
var input = use('controller')
|
||||
var config = use('config')
|
||||
var color = use('color')
|
||||
|
||||
var bunnyTex = graphics.texture("bunny")
|
||||
|
||||
@@ -66,5 +65,5 @@ this.hud = function() {
|
||||
draw.images(bunnyTex, bunnies)
|
||||
|
||||
var msg = 'FPS: ' + fpsAvg.toFixed(2) + ' Bunnies: ' + bunnies.length
|
||||
draw.text(msg, {x:0, y:0, width:config.width, height:40}, null, 0, color.white, 0)
|
||||
draw.text(msg, {x:0, y:0, width:config.width, height:40}, undefined, 0, Color.white, 0)
|
||||
}
|
||||
|
Before Width: | Height: | Size: 390 B After Width: | Height: | Size: 390 B |
|
Before Width: | Height: | Size: 438 B After Width: | Height: | Size: 438 B |
|
Before Width: | Height: | Size: 398 B After Width: | Height: | Size: 398 B |
|
Before Width: | Height: | Size: 337 B After Width: | Height: | Size: 337 B |
|
Before Width: | Height: | Size: 390 B After Width: | Height: | Size: 390 B |
|
Before Width: | Height: | Size: 379 B After Width: | Height: | Size: 379 B |
57
examples/chess/grid.js
Normal file
@@ -0,0 +1,57 @@
|
||||
var CELLS = Symbol()
|
||||
|
||||
var key = function key(x,y) { return `${x},${y}` }
|
||||
|
||||
function grid(w, h)
|
||||
{
|
||||
this[CELLS] = new Map()
|
||||
this.width = w;
|
||||
this.height = h;
|
||||
}
|
||||
|
||||
grid.prototype = {
|
||||
cell(x,y) {
|
||||
var k = key(x,y)
|
||||
if (!this[CELLS].has(k)) this[CELLS].set(k,[])
|
||||
return this[CELLS].get(k)
|
||||
},
|
||||
|
||||
add(entity, pos) {
|
||||
this.cell(pos.x, pos.y).push(entity);
|
||||
entity.coord = pos.slice();
|
||||
},
|
||||
|
||||
remove(entity, pos) {
|
||||
var c = this.cell(pos.x, pos.y);
|
||||
c.splice(c.indexOf(entity), 1);
|
||||
},
|
||||
|
||||
at(pos) {
|
||||
return this.cell(pos.x, pos.y);
|
||||
},
|
||||
|
||||
inBounds(pos) {
|
||||
return pos.x >= 0 && pos.x < this.width && pos.y >= 0 && pos.y < this.height;
|
||||
},
|
||||
|
||||
each(fn) {
|
||||
for (var [k, list] of this[CELLS])
|
||||
for (var p of list) fn(p, p.coord);
|
||||
},
|
||||
|
||||
toString() {
|
||||
var out = `grid [${this.width}x${this.height}]
|
||||
`
|
||||
for (var y = 0; y < this.height; y++) {
|
||||
for (var x = 0; x < this.width; x++) {
|
||||
var cell = this.at([x,y]);
|
||||
out += cell.length
|
||||
}
|
||||
if (y !== this.height - 1) out += "\n"
|
||||
}
|
||||
|
||||
return out
|
||||
},
|
||||
}
|
||||
|
||||
return grid
|
||||
@@ -1,9 +1,16 @@
|
||||
/* main.js – runs the demo with your prototype-based grid */
|
||||
|
||||
var moth = use('moth', $_.delay)
|
||||
var json = use('json')
|
||||
var draw2d = use('prosperon/draw2d')
|
||||
|
||||
var blob = use('blob')
|
||||
var res = 160
|
||||
var internal_res = 480
|
||||
|
||||
moth.initialize({ width:res, height:res, resolution_x:internal_res, resolution_y:internal_res, mode:'letterbox' });
|
||||
|
||||
var os = use('os');
|
||||
var draw2d = use('draw2d');
|
||||
var gfx = use('graphics');
|
||||
|
||||
/*──── import our pieces + systems ───────────────────────────────────*/
|
||||
var Grid = use('grid'); // your new ctor
|
||||
@@ -41,14 +48,14 @@ function updateTitle() {
|
||||
break;
|
||||
case 'connected':
|
||||
if (myColor) {
|
||||
title += (mover.turn == myColor ? "Your turn (" + myColor + ")" : "Opponent's turn (" + mover.turn + ")");
|
||||
title += (mover.turn === myColor ? "Your turn (" + myColor + ")" : "Opponent's turn (" + mover.turn + ")");
|
||||
} else {
|
||||
title += mover.turn + " turn";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
log.console(title)
|
||||
prosperon.window.title = title
|
||||
}
|
||||
|
||||
// Initialize title
|
||||
@@ -63,11 +70,11 @@ var opponentMousePos = null;
|
||||
var opponentHoldingPiece = false;
|
||||
var opponentSelectPos = null;
|
||||
|
||||
function handleMouseButtonDown(e) {
|
||||
if (e.which != 0) return;
|
||||
prosperon.on('mouse_button_down', function(e) {
|
||||
if (e.which !== 0) return;
|
||||
|
||||
// Don't allow piece selection unless we have an opponent
|
||||
if (gameState != 'connected' || !opponent) return;
|
||||
if (gameState !== 'connected' || !opponent) return;
|
||||
|
||||
var mx = e.mouse.x;
|
||||
var my = e.mouse.y;
|
||||
@@ -76,7 +83,7 @@ function handleMouseButtonDown(e) {
|
||||
if (!grid.inBounds(c)) return;
|
||||
|
||||
var cell = grid.at(c);
|
||||
if (cell.length && cell[0].colour == mover.turn) {
|
||||
if (cell.length && cell[0].colour === mover.turn) {
|
||||
selectPos = c;
|
||||
holdingPiece = true;
|
||||
// Send pickup notification to opponent
|
||||
@@ -89,13 +96,13 @@ function handleMouseButtonDown(e) {
|
||||
} else {
|
||||
selectPos = null;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function handleMouseButtonUp(e) {
|
||||
if (e.which != 0 || !holdingPiece || !selectPos) return;
|
||||
prosperon.on('mouse_button_up', function(e) {
|
||||
if (e.which !== 0 || !holdingPiece || !selectPos) return;
|
||||
|
||||
// Don't allow moves unless we have an opponent and it's our turn
|
||||
if (gameState != 'connected' || !opponent || !isMyTurn) {
|
||||
if (gameState !== 'connected' || !opponent || !isMyTurn) {
|
||||
holdingPiece = false;
|
||||
return;
|
||||
}
|
||||
@@ -110,16 +117,16 @@ function handleMouseButtonUp(e) {
|
||||
}
|
||||
|
||||
if (mover.tryMove(grid.at(selectPos)[0], c)) {
|
||||
log.console("Made move from", selectPos, "to", c);
|
||||
console.log("Made move from", selectPos, "to", c);
|
||||
// Send move to opponent
|
||||
log.console("Sending move to opponent:", opponent);
|
||||
console.log("Sending move to opponent:", opponent);
|
||||
send(opponent, {
|
||||
type: 'move',
|
||||
from: selectPos,
|
||||
to: c
|
||||
});
|
||||
isMyTurn = false; // It's now opponent's turn
|
||||
log.console("Move sent, now opponent's turn");
|
||||
console.log("Move sent, now opponent's turn");
|
||||
selectPos = null;
|
||||
updateTitle();
|
||||
}
|
||||
@@ -132,9 +139,9 @@ function handleMouseButtonUp(e) {
|
||||
type: 'piece_drop'
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function handleMouseMotion(e) {
|
||||
prosperon.on('mouse_motion', function(e) {
|
||||
var mx = e.pos.x;
|
||||
var my = e.pos.y;
|
||||
|
||||
@@ -147,7 +154,7 @@ function handleMouseMotion(e) {
|
||||
hoverPos = c;
|
||||
|
||||
// Send mouse position to opponent in real-time
|
||||
if (opponent && gameState == 'connected') {
|
||||
if (opponent && gameState === 'connected') {
|
||||
send(opponent, {
|
||||
type: 'mouse_move',
|
||||
pos: c,
|
||||
@@ -155,18 +162,7 @@ function handleMouseMotion(e) {
|
||||
selectPos: selectPos
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeyDown(e) {
|
||||
// S key - start server
|
||||
if (e.scancode == 22 && gameState == 'waiting') { // S key
|
||||
startServer();
|
||||
}
|
||||
// J key - join server
|
||||
else if (e.scancode == 13 && gameState == 'waiting') { // J key
|
||||
joinServer();
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/*──── drawing helpers ───────────────────────────────────────────────*/
|
||||
/* ── constants ─────────────────────────────────────────────────── */
|
||||
@@ -181,8 +177,8 @@ var opponentMouseColor = [1.0, 0.0, 0.0, 1.0]; // Red for opponent mouse
|
||||
function drawBoard() {
|
||||
for (var y = 0; y < 8; ++y)
|
||||
for (var x = 0; x < 8; ++x) {
|
||||
var isMyHover = hoverPos && hoverPos[0] == x && hoverPos[1] == y;
|
||||
var isOpponentHover = opponentMousePos && opponentMousePos[0] == x && opponentMousePos[1] == y;
|
||||
var isMyHover = hoverPos && hoverPos[0] === x && hoverPos[1] === y;
|
||||
var isOpponentHover = opponentMousePos && opponentMousePos[0] === x && opponentMousePos[1] === y;
|
||||
var isValidMove = selectPos && holdingPiece && isValidMoveForTurn(selectPos, [x, y]);
|
||||
|
||||
var color = ((x+y)&1) ? dark : light;
|
||||
@@ -197,8 +193,7 @@ function drawBoard() {
|
||||
|
||||
draw2d.rectangle(
|
||||
{ x: x*S, y: y*S, width: S, height: S },
|
||||
{ thickness: 0 },
|
||||
{ color: color }
|
||||
{ thickness: 0, color: color }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -211,7 +206,7 @@ function isValidMoveForTurn(from, to) {
|
||||
|
||||
// Check if the destination has a piece of the same color
|
||||
var destCell = grid.at(to);
|
||||
if (destCell.length && destCell[0].colour == piece.colour) {
|
||||
if (destCell.length && destCell[0].colour === piece.colour) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -225,22 +220,22 @@ function drawPieces() {
|
||||
|
||||
// Skip drawing the piece being held (by me or opponent)
|
||||
if (holdingPiece && selectPos &&
|
||||
piece.coord[0] == selectPos[0] &&
|
||||
piece.coord[1] == selectPos[1]) {
|
||||
piece.coord[0] === selectPos[0] &&
|
||||
piece.coord[1] === selectPos[1]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip drawing the piece being held by opponent
|
||||
if (opponentHoldingPiece && opponentSelectPos &&
|
||||
piece.coord[0] == opponentSelectPos[0] &&
|
||||
piece.coord[1] == opponentSelectPos[1]) {
|
||||
piece.coord[0] === opponentSelectPos[0] &&
|
||||
piece.coord[1] === opponentSelectPos[1]) {
|
||||
return;
|
||||
}
|
||||
|
||||
var r = { x: piece.coord[0]*S, y: piece.coord[1]*S,
|
||||
width:S, height:S };
|
||||
|
||||
draw2d.image(piece.sprite, r);
|
||||
draw2d.image(piece.sprite, r, 0, [0,0], [0,0], {mode:"nearest"});
|
||||
});
|
||||
|
||||
// Draw the held piece at the mouse position if we're holding one
|
||||
@@ -250,7 +245,7 @@ function drawPieces() {
|
||||
var r = { x: hoverPos[0]*S, y: hoverPos[1]*S,
|
||||
width:S, height:S };
|
||||
|
||||
draw2d.image(piece.sprite, r);
|
||||
draw2d.image(piece.sprite, r, 0, [0,0], [0,0], {mode:"nearest"});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,23 +257,29 @@ function drawPieces() {
|
||||
width:S, height:S };
|
||||
|
||||
// Draw with slight transparency to show it's the opponent's piece
|
||||
draw2d.image(opponentPiece.sprite, r);
|
||||
draw2d.image(opponentPiece.sprite, r, 0, [0,0], [0,0], {mode:"nearest", color: [1, 1, 1, 0.7]});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function update(dt)
|
||||
{
|
||||
return {}
|
||||
}
|
||||
var graphics = use('graphics')
|
||||
|
||||
function draw()
|
||||
{
|
||||
draw2d.clear()
|
||||
prosperon.on('draw', function() {
|
||||
drawBoard()
|
||||
drawPieces()
|
||||
return draw2d.get_commands()
|
||||
}
|
||||
draw2d.text("HELL", [100,100])
|
||||
})
|
||||
|
||||
prosperon.on('key_down', function(e) {
|
||||
// S key - start server
|
||||
if (e.scancode === 22 && gameState === 'waiting') { // S key
|
||||
startServer();
|
||||
}
|
||||
// J key - join server
|
||||
else if (e.scancode === 13 && gameState === 'waiting') { // J key
|
||||
joinServer();
|
||||
}
|
||||
})
|
||||
|
||||
function startServer() {
|
||||
gameState = 'server_waiting';
|
||||
@@ -288,11 +289,11 @@ function startServer() {
|
||||
updateTitle();
|
||||
|
||||
$_.portal(e => {
|
||||
log.console("Portal received contact message");
|
||||
console.log("Portal received contact message");
|
||||
// Reply with this actor to establish connection
|
||||
log.console (json.encode($_))
|
||||
console.log (json.encode($_))
|
||||
send(e, $_);
|
||||
log.console("Portal replied with server actor");
|
||||
console.log("Portal replied with server actor");
|
||||
}, 5678);
|
||||
}
|
||||
|
||||
@@ -301,10 +302,10 @@ function joinServer() {
|
||||
updateTitle();
|
||||
|
||||
function contact_fn(actor, reason) {
|
||||
log.console("CONTACTED!", actor ? "SUCCESS" : "FAILED", reason);
|
||||
console.log("CONTACTED!", actor ? "SUCCESS" : "FAILED", reason);
|
||||
if (actor) {
|
||||
opponent = actor;
|
||||
log.console("Connection established with server, sending join request");
|
||||
console.log("Connection established with server, sending join request");
|
||||
|
||||
// Send a greet message with our actor object
|
||||
send(opponent, {
|
||||
@@ -312,7 +313,7 @@ function joinServer() {
|
||||
client_actor: $_
|
||||
});
|
||||
} else {
|
||||
log.console(`Failed to connect: ${json.encode(reason)}`);
|
||||
console.log(`Failed to connect: ${json.encode(reason)}`);
|
||||
gameState = 'waiting';
|
||||
updateTitle();
|
||||
}
|
||||
@@ -324,38 +325,51 @@ function joinServer() {
|
||||
});
|
||||
}
|
||||
|
||||
var os = use('os')
|
||||
var actor = use('actor')
|
||||
for (var i in actor) console.log(i)
|
||||
|
||||
// Set up IO actor subscription
|
||||
var ioguy = {
|
||||
__ACTORDATA__: {
|
||||
id: actor.ioactor()
|
||||
}
|
||||
};
|
||||
|
||||
send(ioguy, {
|
||||
type: "subscribe",
|
||||
actor: $_
|
||||
});
|
||||
|
||||
$_.receiver(e => {
|
||||
if (e.kind == 'update')
|
||||
send(e, update(e.dt))
|
||||
else if (e.kind == 'draw')
|
||||
send(e, draw())
|
||||
else if (e.type == 'game_start' || e.type == 'move' || e.type == 'greet')
|
||||
log.console("Receiver got message:", e.type, e);
|
||||
if (e.type === 'game_start' || e.type === 'move' || e.type === 'greet')
|
||||
console.log("Receiver got message:", e.type, e);
|
||||
if (e.type === 'quit') os.exit()
|
||||
|
||||
if (e.type == 'greet') {
|
||||
log.console("Server received greet from client");
|
||||
if (e.type === 'greet') {
|
||||
console.log("Server received greet from client");
|
||||
// Store the client's actor object for ongoing communication
|
||||
opponent = e.client_actor;
|
||||
log.console("Stored client actor:", json.encode(opponent));
|
||||
console.log("Stored client actor:", json.encode(opponent));
|
||||
gameState = 'connected';
|
||||
updateTitle();
|
||||
|
||||
// Send game_start to the client
|
||||
log.console("Sending game_start to client");
|
||||
console.log("Sending game_start to client");
|
||||
send(opponent, {
|
||||
type: 'game_start',
|
||||
your_color: 'black'
|
||||
});
|
||||
log.console("game_start message sent to client");
|
||||
console.log("game_start message sent to client");
|
||||
}
|
||||
else if (e.type == 'game_start') {
|
||||
log.console("Game starting, I am:", e.your_color);
|
||||
else if (e.type === 'game_start') {
|
||||
console.log("Game starting, I am:", e.your_color);
|
||||
myColor = e.your_color;
|
||||
isMyTurn = (myColor == 'white');
|
||||
isMyTurn = (myColor === 'white');
|
||||
gameState = 'connected';
|
||||
updateTitle();
|
||||
} else if (e.type == 'move') {
|
||||
log.console("Received move from opponent:", e.from, "to", e.to);
|
||||
} else if (e.type === 'move') {
|
||||
console.log("Received move from opponent:", e.from, "to", e.to);
|
||||
// Apply opponent's move
|
||||
var fromCell = grid.at(e.from);
|
||||
if (fromCell.length) {
|
||||
@@ -363,33 +377,27 @@ $_.receiver(e => {
|
||||
if (mover.tryMove(piece, e.to)) {
|
||||
isMyTurn = true; // It's now our turn
|
||||
updateTitle();
|
||||
log.console("Applied opponent move, now my turn");
|
||||
console.log("Applied opponent move, now my turn");
|
||||
} else {
|
||||
log.console("Failed to apply opponent move");
|
||||
console.log("Failed to apply opponent move");
|
||||
}
|
||||
} else {
|
||||
log.console("No piece found at from position");
|
||||
console.log("No piece found at from position");
|
||||
}
|
||||
} else if (e.type == 'mouse_move') {
|
||||
} else if (e.type === 'mouse_move') {
|
||||
// Update opponent's mouse position
|
||||
opponentMousePos = e.pos;
|
||||
opponentHoldingPiece = e.holding;
|
||||
opponentSelectPos = e.selectPos;
|
||||
} else if (e.type == 'piece_pickup') {
|
||||
} else if (e.type === 'piece_pickup') {
|
||||
// Opponent picked up a piece
|
||||
opponentSelectPos = e.pos;
|
||||
opponentHoldingPiece = true;
|
||||
} else if (e.type == 'piece_drop') {
|
||||
} else if (e.type === 'piece_drop') {
|
||||
// Opponent dropped their piece
|
||||
opponentHoldingPiece = false;
|
||||
opponentSelectPos = null;
|
||||
} else if (e.type == 'mouse_button_down') {
|
||||
handleMouseButtonDown(e)
|
||||
} else if (e.type == 'mouse_button_up') {
|
||||
handleMouseButtonUp(e)
|
||||
} else if (e.type == 'mouse_motion') {
|
||||
handleMouseMotion(e)
|
||||
} else if (e.type == 'key_down') {
|
||||
handleKeyDown(e)
|
||||
}
|
||||
})
|
||||
|
||||
prosperon.dispatch(e.type, e)
|
||||
})
|
||||
@@ -5,17 +5,17 @@ var MovementSystem = function(grid, rules) {
|
||||
}
|
||||
|
||||
MovementSystem.prototype.tryMove = function (piece, to) {
|
||||
if (piece.colour != this.turn) return false;
|
||||
if (piece.colour !== this.turn) return false;
|
||||
|
||||
// normalise ‘to’ into our hybrid coord
|
||||
var dest = [to.x ?? t[0],
|
||||
to.y ?? to[1]];
|
||||
var dest = [to.x !== undefined ? to.x : to[0],
|
||||
to.y !== undefined ? to.y : to[1]];
|
||||
|
||||
if (!this.grid.inBounds(dest)) return false;
|
||||
if (!this.rules.canMove(piece, piece.coord, dest, this.grid)) return false;
|
||||
|
||||
var victims = this.grid.at(dest);
|
||||
if (victims.length && victims[0].colour == piece.colour) return false;
|
||||
if (victims.length && victims[0].colour === piece.colour) return false;
|
||||
if (victims.length) victims[0].captured = true;
|
||||
|
||||
this.grid.remove(piece, piece.coord);
|
||||
@@ -25,7 +25,7 @@ MovementSystem.prototype.tryMove = function (piece, to) {
|
||||
piece.coord.x = dest.x;
|
||||
piece.coord.y = dest.y;
|
||||
|
||||
this.turn = (this.turn == 'white') ? 'black' : 'white';
|
||||
this.turn = (this.turn === 'white') ? 'black' : 'white';
|
||||
return true;
|
||||
};
|
||||
|
||||
45
examples/chess/rules.js
Normal file
@@ -0,0 +1,45 @@
|
||||
/* helper – robust coord access */
|
||||
function cx(c) { return (c.x !== undefined) ? c.x : c[0]; }
|
||||
function cy(c) { return (c.y !== undefined) ? c.y : c[1]; }
|
||||
|
||||
/* simple move-shape checks */
|
||||
var deltas = {
|
||||
pawn: function (pc, dx, dy, grid, to) {
|
||||
var dir = (pc.colour === 'white') ? -1 : 1;
|
||||
var base = (pc.colour === 'white') ? 6 : 1;
|
||||
var one = (dy === dir && dx === 0 && grid.at(to).length === 0);
|
||||
var two = (dy === 2 * dir && dx === 0 && cy(pc.coord) === base &&
|
||||
grid.at({ x: cx(pc.coord), y: cy(pc.coord)+dir }).length === 0 &&
|
||||
grid.at(to).length === 0);
|
||||
var cap = (dy === dir && Math.abs(dx) === 1 && grid.at(to).length);
|
||||
return one || two || cap;
|
||||
},
|
||||
rook : function (pc, dx, dy) { return (dx === 0 || dy === 0); },
|
||||
bishop: function (pc, dx, dy) { return Math.abs(dx) === Math.abs(dy); },
|
||||
queen : function (pc, dx, dy) { return (dx === 0 || dy === 0 || Math.abs(dx) === Math.abs(dy)); },
|
||||
knight: function (pc, dx, dy) { return (Math.abs(dx) === 1 && Math.abs(dy) === 2) ||
|
||||
(Math.abs(dx) === 2 && Math.abs(dy) === 1); },
|
||||
king : function (pc, dx, dy) { return Math.max(Math.abs(dx), Math.abs(dy)) === 1; }
|
||||
};
|
||||
|
||||
function clearLine(from, to, grid) {
|
||||
var dx = Math.sign(cx(to) - cx(from));
|
||||
var dy = Math.sign(cy(to) - cy(from));
|
||||
var x = cx(from) + dx, y = cy(from) + dy;
|
||||
while (x !== cx(to) || y !== cy(to)) {
|
||||
if (grid.at({ x: x, y: y }).length) return false;
|
||||
x += dx; y += dy;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function canMove(piece, from, to, grid) {
|
||||
var dx = cx(to) - cx(from);
|
||||
var dy = cy(to) - cy(from);
|
||||
var f = deltas[piece.kind];
|
||||
if (!f || !f(piece, dx, dy, grid, to)) return false;
|
||||
if (piece.kind === 'knight') return true;
|
||||
return clearLine(from, to, grid);
|
||||
}
|
||||
|
||||
return { canMove };
|
||||
|
Before Width: | Height: | Size: 376 B After Width: | Height: | Size: 376 B |
|
Before Width: | Height: | Size: 403 B After Width: | Height: | Size: 403 B |
|
Before Width: | Height: | Size: 381 B After Width: | Height: | Size: 381 B |
|
Before Width: | Height: | Size: 313 B After Width: | Height: | Size: 313 B |
|
Before Width: | Height: | Size: 378 B After Width: | Height: | Size: 378 B |
|
Before Width: | Height: | Size: 378 B After Width: | Height: | Size: 378 B |
@@ -18,7 +18,7 @@ var state = {
|
||||
|
||||
// Helper to calculate progress percentage
|
||||
function get_progress() {
|
||||
if (state.total_bytes == 0) {
|
||||
if (state.total_bytes === 0) {
|
||||
return 0;
|
||||
}
|
||||
return Math.round((state.downloaded_bytes / state.total_bytes) * 100);
|
||||
@@ -101,7 +101,7 @@ $_.receiver(function(msg) {
|
||||
break;
|
||||
|
||||
case 'status':
|
||||
log.console(`got status request. current is ${json.encode(get_status())}`)
|
||||
console.log(`got status request. current is ${json.encode(get_status())}`)
|
||||
send(msg, {
|
||||
type: 'status_response',
|
||||
...get_status()
|
||||
@@ -150,7 +150,7 @@ function read_next_chunk() {
|
||||
try {
|
||||
var chunk = http.fetch_read_chunk(state.connection);
|
||||
|
||||
if (chunk == null) {
|
||||
if (chunk === null) {
|
||||
// Download complete
|
||||
finish_download();
|
||||
return;
|
||||
@@ -8,22 +8,22 @@ var waiting_client = null;
|
||||
var match_id = 0;
|
||||
|
||||
$_.portal(e => {
|
||||
log.console("NAT server: received connection request");
|
||||
console.log("NAT server: received connection request");
|
||||
|
||||
if (!is_actor(e.actor))
|
||||
send(e, {reason: "Must provide the actor you want to connect."});
|
||||
|
||||
if (waiting_client) {
|
||||
log.console(`sending out messages! to ${json.encode(e.actor)} and ${json.encode(waiting_client.actor)}`)
|
||||
console.log(`sending out messages! to ${json.encode(e.actor)} and ${json.encode(waiting_client.actor)}`)
|
||||
send(waiting_client, e.actor)
|
||||
send(e, waiting_client.actor)
|
||||
|
||||
waiting_client = null
|
||||
waiting_client = undefined
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
waiting_client = e
|
||||
|
||||
log.console(`actor ${json.encode(e.actor)} is waiting ...`)
|
||||
console.log(`actor ${json.encode(e.actor)} is waiting ...`)
|
||||
}, 4000);
|
||||
@@ -1,11 +1,11 @@
|
||||
log.console(`nat client starting`)
|
||||
console.log(`nat client starting`)
|
||||
|
||||
$_.contact((actor, reason) => {
|
||||
if (actor) {
|
||||
log.console(`trying to message ${json.encode(actor)}`)
|
||||
console.log(`trying to message ${json.encode(actor)}`)
|
||||
send(actor, {type:"greet"})
|
||||
} else {
|
||||
log.console(json.encode(reason))
|
||||
console.log(json.encode(reason))
|
||||
}
|
||||
}, {
|
||||
address: "108.210.60.32", // NAT server's public IP
|
||||
@@ -16,7 +16,7 @@ $_.contact((actor, reason) => {
|
||||
$_.receiver(e => {
|
||||
switch(e.type) {
|
||||
case 'greet':
|
||||
log.console(`hello!`)
|
||||
console.log(`hello!`)
|
||||
break
|
||||
}
|
||||
})
|
||||
@@ -2,7 +2,6 @@
|
||||
var draw = use('draw2d')
|
||||
var input = use('controller')
|
||||
var config = use('config')
|
||||
var color = use('color')
|
||||
|
||||
prosperon.camera.transform.pos = [0,0]
|
||||
|
||||
@@ -74,13 +73,13 @@ this.hud = function() {
|
||||
draw.rectangle({x:0, y:0, width:config.width, height:config.height}, [0,0,0,1])
|
||||
|
||||
// Draw paddles
|
||||
draw.rectangle({x:p1.x - paddleW*0.5, y:p1.y - paddleH*0.5, width:paddleW, height:paddleH}, color.white)
|
||||
draw.rectangle({x:p2.x - paddleW*0.5, y:p2.y - paddleH*0.5, width:paddleW, height:paddleH}, color.white)
|
||||
draw.rectangle({x:p1.x - paddleW*0.5, y:p1.y - paddleH*0.5, width:paddleW, height:paddleH}, Color.white)
|
||||
draw.rectangle({x:p2.x - paddleW*0.5, y:p2.y - paddleH*0.5, width:paddleW, height:paddleH}, Color.white)
|
||||
|
||||
// Draw ball
|
||||
draw.rectangle({x:ball.x - ball.size*0.5, y:ball.y - ball.size*0.5, width:ball.size, height:ball.size}, color.white)
|
||||
draw.rectangle({x:ball.x - ball.size*0.5, y:ball.y - ball.size*0.5, width:ball.size, height:ball.size}, Color.white)
|
||||
|
||||
// Simple score display
|
||||
var msg = score1 + " " + score2
|
||||
draw.text(msg, {x:0, y:10, width:config.width, height:40}, null, 0, color.white, 0)
|
||||
draw.text(msg, {x:0, y:10, width:config.width, height:40}, undefined, 0, Color.white, 0)
|
||||
}
|
||||
@@ -4,7 +4,6 @@ var render = use('render')
|
||||
var graphics = use('graphics')
|
||||
var input = use('input')
|
||||
var config = use('config')
|
||||
var color = use('color')
|
||||
|
||||
prosperon.camera.transform.pos = [0,0]
|
||||
|
||||
@@ -36,7 +35,7 @@ function spawnApple() {
|
||||
apple = {x:Math.floor(Math.random()*gridW), y:Math.floor(Math.random()*gridH)}
|
||||
// Re-spawn if apple lands on snake
|
||||
for (var i=0; i<snake.length; i++)
|
||||
if (snake[i].x == apple.x && snake[i].y == apple.y) { spawnApple(); return }
|
||||
if (snake[i].x === apple.x && snake[i].y === apple.y) { spawnApple(); return }
|
||||
}
|
||||
|
||||
function wrap(pos) {
|
||||
@@ -49,7 +48,7 @@ function wrap(pos) {
|
||||
resetGame()
|
||||
|
||||
this.update = function(dt) {
|
||||
if (gameState != "playing") return
|
||||
if (gameState !== "playing") return
|
||||
moveTimer += dt
|
||||
if (moveTimer < moveInterval) return
|
||||
moveTimer -= moveInterval
|
||||
@@ -63,7 +62,7 @@ this.update = function(dt) {
|
||||
|
||||
// Check collision with body
|
||||
for (var i=0; i<snake.length; i++) {
|
||||
if (snake[i].x == head.x && snake[i].y == head.y) {
|
||||
if (snake[i].x === head.x && snake[i].y === head.y) {
|
||||
gameState = "gameover"
|
||||
return
|
||||
}
|
||||
@@ -73,7 +72,7 @@ this.update = function(dt) {
|
||||
snake.unshift(head)
|
||||
|
||||
// Eat apple?
|
||||
if (head.x == apple.x && head.y == apple.y) spawnApple()
|
||||
if (head.x === apple.x && head.y === apple.y) spawnApple()
|
||||
else snake.pop()
|
||||
}
|
||||
|
||||
@@ -84,15 +83,15 @@ this.hud = function() {
|
||||
// Draw snake
|
||||
for (var i=0; i<snake.length; i++) {
|
||||
var s = snake[i]
|
||||
draw.rectangle({x:s.x*cellSize, y:s.y*cellSize, width:cellSize, height:cellSize}, color.green)
|
||||
draw.rectangle({x:s.x*cellSize, y:s.y*cellSize, width:cellSize, height:cellSize}, Color.green)
|
||||
}
|
||||
|
||||
// Draw apple
|
||||
draw.rectangle({x:apple.x*cellSize, y:apple.y*cellSize, width:cellSize, height:cellSize}, color.red)
|
||||
draw.rectangle({x:apple.x*cellSize, y:apple.y*cellSize, width:cellSize, height:cellSize}, Color.red)
|
||||
|
||||
if (gameState == "gameover") {
|
||||
if (gameState === "gameover") {
|
||||
var msg = "GAME OVER! Press SPACE to restart."
|
||||
draw.text(msg, {x:0, y:config.height*0.5-10, width:config.width, height:20}, null, 0, color.white)
|
||||
draw.text(msg, {x:0, y:config.height*0.5-10, width:config.width, height:20}, undefined, 0, Color.white)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,19 +99,19 @@ this.hud = function() {
|
||||
// "Up" means y=1, so going physically up on screen
|
||||
this.inputs = {
|
||||
up: function() {
|
||||
if (direction.y != -1) nextDirection = {x:0,y:1}
|
||||
if (direction.y !== -1) nextDirection = {x:0,y:1}
|
||||
},
|
||||
down: function() {
|
||||
if (direction.y != 1) nextDirection = {x:0,y:-1}
|
||||
if (direction.y !== 1) nextDirection = {x:0,y:-1}
|
||||
},
|
||||
left: function() {
|
||||
if (direction.x != 1) nextDirection = {x:-1,y:0}
|
||||
if (direction.x !== 1) nextDirection = {x:-1,y:0}
|
||||
},
|
||||
right: function() {
|
||||
if (direction.x != -1) nextDirection = {x:1,y:0}
|
||||
if (direction.x !== -1) nextDirection = {x:1,y:0}
|
||||
},
|
||||
space: function() {
|
||||
if (gameState=="gameover") resetGame()
|
||||
if (gameState==="gameover") resetGame()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,23 +23,23 @@ var stats_loaded = false;
|
||||
// Initialize Steam
|
||||
function init_steam() {
|
||||
if (!steam) {
|
||||
log.console("Steam module not available");
|
||||
console.log("Steam module not available");
|
||||
return false;
|
||||
}
|
||||
|
||||
log.console("Initializing Steam...");
|
||||
console.log("Initializing Steam...");
|
||||
steam_available = steam.steam_init();
|
||||
|
||||
if (steam_available) {
|
||||
log.console("Steam initialized successfully");
|
||||
console.log("Steam initialized successfully");
|
||||
|
||||
// Request current stats/achievements
|
||||
if (steam.stats.stats_request()) {
|
||||
log.console("Stats requested");
|
||||
console.log("Stats requested");
|
||||
stats_loaded = true;
|
||||
}
|
||||
} else {
|
||||
log.console("Failed to initialize Steam");
|
||||
console.log("Failed to initialize Steam");
|
||||
}
|
||||
|
||||
return steam_available;
|
||||
@@ -59,13 +59,13 @@ function unlock_achievement(achievement_name) {
|
||||
// Check if already unlocked
|
||||
var unlocked = steam.achievement.achievement_get(achievement_name);
|
||||
if (unlocked) {
|
||||
log.console("Achievement already unlocked:", achievement_name);
|
||||
console.log("Achievement already unlocked:", achievement_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Unlock it
|
||||
if (steam.achievement.achievement_set(achievement_name)) {
|
||||
log.console("Achievement unlocked:", achievement_name);
|
||||
console.log("Achievement unlocked:", achievement_name);
|
||||
|
||||
// Store stats to make it permanent
|
||||
steam.stats.stats_store();
|
||||
@@ -87,7 +87,7 @@ function update_stat(stat_name, value, is_float) {
|
||||
}
|
||||
|
||||
if (success) {
|
||||
log.console("Stat updated:", stat_name, "=", value);
|
||||
console.log("Stat updated:", stat_name, "=", value);
|
||||
steam.stats.stats_store();
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ function start_game() {
|
||||
total_score = get_stat(STATS.TOTAL_SCORE, false);
|
||||
current_score = 0;
|
||||
|
||||
log.console("Starting game #" + (games_played + 1));
|
||||
console.log("Starting game #" + (games_played + 1));
|
||||
}
|
||||
|
||||
function end_game(score) {
|
||||
@@ -128,7 +128,7 @@ function end_game(score) {
|
||||
update_stat(STATS.TOTAL_SCORE, total_score, false);
|
||||
|
||||
// Check for achievements
|
||||
if (games_played == 1) {
|
||||
if (games_played === 1) {
|
||||
unlock_achievement(ACHIEVEMENTS.FIRST_WIN);
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ function load_from_cloud() {
|
||||
function cleanup_steam() {
|
||||
if (steam_available) {
|
||||
steam.steam_shutdown();
|
||||
log.console("Steam shut down");
|
||||
console.log("Steam shut down");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
var draw = use('draw2d')
|
||||
var input = use('input')
|
||||
var config = use('config')
|
||||
var color = use('color')
|
||||
|
||||
prosperon.camera.transform.pos = [0,0]
|
||||
|
||||
@@ -128,10 +127,10 @@ function clearLines() {
|
||||
}
|
||||
}
|
||||
// Score
|
||||
if (lines==1) score += 100
|
||||
else if (lines==2) score += 300
|
||||
else if (lines==3) score += 500
|
||||
else if (lines==4) score += 800
|
||||
if (lines===1) score += 100
|
||||
else if (lines===2) score += 300
|
||||
else if (lines===3) score += 500
|
||||
else if (lines===4) score += 800
|
||||
linesCleared += lines
|
||||
level = Math.floor(linesCleared/10)
|
||||
}
|
||||
@@ -153,7 +152,7 @@ spawnPiece()
|
||||
this.update = function(dt) {
|
||||
if (gameOver) return
|
||||
|
||||
// ======= Horizontal Movement Gate =======
|
||||
// ========== Horizontal Movement Gate ==========
|
||||
var leftPressed = input.keyboard.down('a')
|
||||
var rightPressed = input.keyboard.down('d')
|
||||
var horizontalMove = 0
|
||||
@@ -191,7 +190,7 @@ this.update = function(dt) {
|
||||
hMoveTimer -= dt
|
||||
prevLeft = leftPressed
|
||||
prevRight = rightPressed
|
||||
// ======= End Horizontal Movement Gate =======
|
||||
// ========== End Horizontal Movement Gate ==========
|
||||
|
||||
// Rotate with W (once per press, no spinning)
|
||||
if (input.keyboard.down('w')) {
|
||||
@@ -249,7 +248,7 @@ this.hud = function() {
|
||||
}
|
||||
|
||||
// Next piece window
|
||||
draw.text("Next", {x:70, y:5, width:50, height:10}, null, 0, color.white)
|
||||
draw.text("Next", {x:70, y:5, width:50, height:10}, undefined, 0, Color.white)
|
||||
if (nextPiece) {
|
||||
for (var i=0; i<nextPiece.blocks.length; i++) {
|
||||
var nx = nextPiece.blocks[i][0]
|
||||
@@ -262,10 +261,10 @@ this.hud = function() {
|
||||
|
||||
// Score & Level
|
||||
var info = "Score: " + score + "\nLines: " + linesCleared + "\nLevel: " + level
|
||||
draw.text(info, {x:70, y:30, width:90, height:50}, null, 0, color.white)
|
||||
draw.text(info, {x:70, y:30, width:90, height:50}, undefined, 0, Color.white)
|
||||
|
||||
if (gameOver) {
|
||||
draw.text("GAME OVER", {x:10, y:config.height*0.5-5, width:config.width-20, height:20}, null, 0, color.red)
|
||||
draw.text("GAME OVER", {x:10, y:config.height*0.5-5, width:config.width-20, height:20}, undefined, 0, Color.red)
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 867 B After Width: | Height: | Size: 867 B |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |