From 50de3c5d77cff04af5a6483c39227e01dfa29cd9 Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Sun, 3 Jun 2018 04:30:28 +0300 Subject: [PATCH] moving Slang to its own repo... Signed-off-by: Alex A. Naanou --- README.md | 6 +- Slang/README.md | 4 - Slang/manifest.json | 7 - Slang/slang.appcache | 11 - Slang/slang.html | 279 ------ Slang/slang.js | 1134 ------------------------- TeachersTimer/index.html | 222 ----- js-oop-constructors.jpg | Bin 18909 -> 0 bytes js-oop.js | 706 --------------- jsssnake/jsssnake-generator-test.html | 538 ------------ jsssnake/jsssnake-test.html | 522 ------------ jsssnake/jsssnake-test.js | 144 ---- jsssnake/jsssnake.html | 536 ------------ jsssnake/jsssnake.js | 423 --------- simplesnake/manifest.json | 7 - simplesnake/simplesnake.appcache | 12 - simplesnake/simplesnake.css | 165 ---- simplesnake/simplesnake.html | 22 - simplesnake/simplesnake.js | 556 ------------ 19 files changed, 4 insertions(+), 5290 deletions(-) delete mode 100644 Slang/README.md delete mode 100755 Slang/manifest.json delete mode 100755 Slang/slang.appcache delete mode 100755 Slang/slang.html delete mode 100755 Slang/slang.js delete mode 100755 TeachersTimer/index.html delete mode 100755 js-oop-constructors.jpg delete mode 100755 js-oop.js delete mode 100755 jsssnake/jsssnake-generator-test.html delete mode 100755 jsssnake/jsssnake-test.html delete mode 100755 jsssnake/jsssnake-test.js delete mode 100755 jsssnake/jsssnake.html delete mode 100755 jsssnake/jsssnake.js delete mode 100644 simplesnake/manifest.json delete mode 100755 simplesnake/simplesnake.appcache delete mode 100755 simplesnake/simplesnake.css delete mode 100644 simplesnake/simplesnake.html delete mode 100755 simplesnake/simplesnake.js diff --git a/README.md b/README.md index 86fb34b..7a441e1 100755 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ -Course-JavaScript -================= \ No newline at end of file +# Slang + +For and interactive version of Slang interpreter go here: +http://flynx.github.io/Course-JavaScript/Slang/slang.html diff --git a/Slang/README.md b/Slang/README.md deleted file mode 100644 index 7a441e1..0000000 --- a/Slang/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Slang - -For and interactive version of Slang interpreter go here: -http://flynx.github.io/Course-JavaScript/Slang/slang.html diff --git a/Slang/manifest.json b/Slang/manifest.json deleted file mode 100755 index 4276016..0000000 --- a/Slang/manifest.json +++ /dev/null @@ -1,7 +0,0 @@ - -{ - "short_name": "Slang", - "name": "Slang", - "display": "standalone", - "Theme_color": "white" -} diff --git a/Slang/slang.appcache b/Slang/slang.appcache deleted file mode 100755 index 9f31719..0000000 --- a/Slang/slang.appcache +++ /dev/null @@ -1,11 +0,0 @@ -CACHE MANIFEST -# Timestamp: 20170929020625 - -CACHE: -slang.html -slang.js -manifest.json - -NETWORK: -* - diff --git a/Slang/slang.html b/Slang/slang.html deleted file mode 100755 index 4896af6..0000000 --- a/Slang/slang.html +++ /dev/null @@ -1,279 +0,0 @@ - - - -Slang - - - - - - - - - - - - - -

Slang

- Toggle bootstrap code view... -
-

Available words

-

- This section includes constants (red) and constant-like - words (words that allways yield the same value), - built-in words (blue), and 2'nd gen words (black): -

-

-

Slang Console

-
-
-
-
- - - diff --git a/Slang/slang.js b/Slang/slang.js deleted file mode 100755 index 17809a8..0000000 --- a/Slang/slang.js +++ /dev/null @@ -1,1134 +0,0 @@ -/********************************************************************** -* -* -* -**********************************************************************/ - -/* XXX for some odd reason this breaks the interpreter... -Array.prototype.toString = function(){ - return '[ ' + this.join(', ') + ' ]' -} -*/ - - -/*********************************************************************/ - -function run(context){ - - context.stack = context.stack == null ? [] : context.stack - - while(context.code.length > 0){ - - var cur = context.code.splice(0, 1)[0] - - // exit... - if(typeof(cur) == typeof('abc') && cur == '_exit'){ - return context - - // word - } else if(typeof(cur) == typeof('abc') && cur in context.ns){ - var word = context.ns[cur] - // low-level word... - if(typeof(word) == typeof(function(){})){ - var res = context.ns[cur](context) - - // hi-level word... - } else if(typeof(word) == typeof([]) && word && word.constructor.name == 'Array'){ - // XXX add isolation with a continuation... - context.code.splice.apply(context.code, [0, 0].concat(word)) - var res = undefined - - // variable... - } else { - var res = word - } - - if(res !== undefined){ - context.stack.push(res) - } - - // everything else... - } else { - context.stack.push(cur) - } - } - - return context -} - -// XXX make this add '\n' / EOL words to the stream... -//var SPLITTER = /\s*\([^\)]*\)\s*|\s*--.*[\n$]|\s*"([^"]*)"\s*|\s*'([^']*)'\s*|\s+/m -var SPLITTER = RegExp([ - /* XXX there are two ways to deal with comments: - // 1) lexer-based -- this section commented, next uncommented... - // 2) macro-based -- this section uncommented, next commented... - // #2 is a bit buggy... - // terms to keep in the stream... - '\\s*('+[ - '\\n', - '--', - ].join('|')+')', - //*/ - - //* lexer comments... - '\\s*\\([^\\)]*\\)\\s*', - '\\s*--.*[\\n$]', - //*/ - - // quoted strings... - '\\s*"([^"]*)"\\s*', - "\\s*'([^']*)'\\s*", - - // quote... - '\\s*(\\\\)', - - // whitespace... - '\\s+', - ].join('|'), - 'm') - - -// helpers... -// XXX should these skip quoted ends? -var collect = function(start, end){ - return function(context){ - var res = start ? [start] : [] - var code = context.code - var cur = code.shift() - res.push(cur) - while(cur != end && code.length > 0){ - cur = code.shift() - res.push(cur) - } - return res - } -} -var drop = function(start, end){ - var collector = collect(start, end) - //return function(context){ collector(context) } - return function(context){ console.log('XXX', collector(context).join(' ')) } -} - - -// pre-processor namespace... -var PRE_NAMESPACE = { - // comment... - // syntax: -- ... \n - // - // drop everything until '\n' - // - // NOTE: this depends on explicit '\n' words... - //'--': drop('--', '\n'), - '(': drop('(', ')'), - - // XXX use the real reader... - // block... - // syntax: [ ... ] - // NOTE: the shorthand ']]' will close ALL the open blocks. - // XXX should ']]' be smart enough to look ahead and close only the - // blocks not explicitly closed later??? - // ..see below for more... - '[': function(context){ - var block = [] - var code = context.code - var cur = code.splice(0, 1)[0] - while(cur != ']' && cur != ']]' && code.length > 0){ - if(cur == '['){ - cur = this['['](context) - } - block.push(cur) - cur = code.splice(0, 1)[0] - } - // we need this to be able to jump out of all the nested blocks, - // thus we'll keep the ']]' in code and remove it explicitly - // later... - if(cur == ']]'){ - // XXX should we look ahead and count the explicitly closed - // via ']' and ']]' blocks??? - // ...at this point this seems a bit complex... - // ...if there are more than one ']]' in a structure - // this might stop being deterministic... - code.splice(0, 0, cur) - } - if(code.length == 0 && cur != ']' && cur != ']]'){ - console.error('Did not find expected "]".') - } - return block - }, - // drop the closing words... - ']]': function(context){}, - ']': function(context){ console.error('Unexpected "]".') }, - - // XXX macros are not recursive... - 'macro:': function(context){ - var ident = context.code.splice(0, 1) - var cur = context.code.splice(0, 1) - - // as we do not have blocks yet we need to manually collect one ;) - if(cur[0] == '['){ - cur = [ this['['](context) ] - } - - this[ident] = cur[0] - }, - - // a no op... - '\n': function(){ }, -} - - -// main namespace... -var NAMESPACE = { - // constants... - 'true': true, - 'false': false, - 'null': 'null', - - 'nop': function(){}, - - 'is?': function(context){ - return context.stack.pop() === context.stack.pop() }, - - // XXX experimental... - // flip the code and stack... - // ... -- ... - '_flip': function(context){ - var stack = context.stack - context.stack = context.code.reverse() - context.code = stack.reverse() - }, - - // swap heads of stack and code - // ... ns nc -- ... - '_swapN': function(context){ - var c = context.stack.pop() - var s = context.stack.pop() - var l = context.stack.length - - // get the stack/code sections to swap... - var s_c = context.stack.splice(l-s, l) - var c_c = context.code.splice(0, c) - var l = context.stack.length - - // we need to pad e_c and c_c to requested length... - s_c = s_c.length < s ? s_c.concat(Array( s - s_c.length )) : s_c - c_c = c_c.length < c ? c_c.concat(Array( c - c_c.length )) : c_c - - // XXX we also need to shove something more sensible in the - // padding that undefined... - - context.code.splice.apply(context.code, [0, 0].concat(s_c)) - context.stack.splice.apply(context.stack, [l, 0].concat(c_c)) - }, - - // encapsulate stack to a block... - // ... -- [ ... ] - 's2b': function(context){ - context.stack = [ context.stack ] }, - // expand block to stack... - // NOTE: this will append the block contents to stack, w.o. replacing - // stack contents. this is different to _s2b - // ... [ ... ] -- ... ... - 'b2s': function(context){ - var c = context.stack.pop() - c = c === undefined ? [] : c - context.stack.splice.apply(context.stack, [context.stack.length, 0].concat(c)) - }, - 'print': function(context){ - console.log('>>>', context.stack[context.stack.length-1]) }, - - // turn a sting into a lexical list... - // c -- b - // XXX BUG see code... - 'lex': function(context){ - code = context.stack.pop() - if(typeof(code) == typeof('abc')){ - // XXX BUG: '"aaa" "bbb"' translates to ['"aaa"', '" "', '"bbb"'] - // i.e. quotes w/o whitespace are eaten... - if(/^\s*(['"]).*\1\s*$/m.test(code)){ - code = code.split(/^\s*(['"])(.*)\1\s*$/m)[2] - } - - //console.log(code) - - var res = [] - code = code - // split by strings whitespace and block comments... - .split(SPLITTER) - // parse numbers... - .map(function(e){ - // numbers... - if(/^[-+]?[0-9]+\.[0-9]+$/.test(e)){ - e = parseFloat(e) - } else if(/^[-+]?[0-9]+$/.test(e)){ - e = parseInt(e) - } - return e - }) - // remove undefined groups... - .filter(function(e){ - // NOTE: in JS 0 == '' is true ;) - return e !== undefined && e !== '' - }) - } - return code - }, - // pre-process a lexical list... - // a -- b - 'prep': function(context){ - var code = context.stack.pop() - - return run({ - stack: [], - code: code, - ns: context.pre_ns, - pre_ns: {}, - }).stack - }, - - // s c -- s - '_exec': function(context){ - // block... - var b = context.stack.pop() - if(typeof(b) == typeof([]) && b && b.constructor.name == 'Array'){ - b = b.slice() - } else { - b = [ b ] - } - // stack... - var s = context.stack.pop() - var res = run({ - stack: s, - code: b, - // NOTE: this can have side-effects on the context... - ns: context.ns, - pre_ns: context.pre_ns - }) - // XXX is this the right way to go? - context.ns = res.ns - context.pre_ns = res.pre_ns - return res.stack - }, - // quote - push the next elem as-is to stack... - // -- x - '\\': function(context){ - return context.code.splice(0, 1)[0] }, - - // comparisons and logic... - // a b -- c - 'and': function(context){ - var b = context.stack.pop() - var a = context.stack.pop() - if(a){ - return b - } else { - return a - } - }, - // a b -- c - 'or': function(context){ - var b = context.stack.pop() - var a = context.stack.pop() - if(a){ - return a - } else { - return b - } - }, - // x -- b - 'not': function(context){ - return !context.stack.pop() }, - // a b -- c - 'gt': function(context){ - return context.stack.pop() < context.stack.pop() }, - // a b -- c - 'eq': function(context){ - return context.stack.pop() == context.stack.pop() }, - - // stack operations... - // ... x -- x ... - 'rot': function(context){ - context.stack.splice(0, 0, context.stack.pop()) }, - // x ... -- ... x - 'tor': function(context){ - context.stack.push(context.stack.shift()) }, - // a b -- b a - 'swap': function(context){ - return context.stack.splice(context.stack.length-2, 1)[0] }, - // x -- x x - 'dup': function(context){ - return context.stack[context.stack.length-1] }, - // x -- x' - // NOTE: this will do a deep copy... - 'clone': function(context){ - return JSON.parse(JSON.stringify(context.stack.pop())) }, - // x -- - 'drop': function(context){ - context.stack.pop() }, - - // a -- b - // NOTE: all names are also strings so moo string? and 'moo' string? - // are the same... - 'string?': function(context){ - return typeof(context.stack.pop()) == typeof('str') }, - - // basic number operations... - // a -- b - 'number?': function(context){ - return typeof(context.stack.pop()) == typeof(123) }, - // a b -- c - 'add': function(context){ - return context.stack.pop() + context.stack.pop() }, - 'sub': function(context){ - return - context.stack.pop() + context.stack.pop() }, - 'mul': function(context){ - return context.stack.pop() * context.stack.pop() }, - 'div': function(context){ - return 1/context.stack.pop() * context.stack.pop() }, - - // block/list operations... - 'block?': function(context){ - var e = context.stack.pop() - return typeof(e) == typeof([]) && e && e.constructor.name == 'Array' - }, - // b n -- b e - 'at': function(context){ - var i = context.stack.pop() - if(i < 0){ - var l = context.stack[context.stack.length-1] - return l[l.length + i] - } - return context.stack[context.stack.length-1][i] - }, - // b e n -- b - 'to': function(context){ - var i = context.stack.pop() - var e = context.stack.pop() - if(i < 0){ - var l = context.stack[context.stack.length-1] - l[l.length + i] = e - } else { - context.stack[context.stack.length-1][i] = e - } - }, - // b e n -- b - 'before': function(context){ - var i = context.stack.pop() - var e = context.stack.pop() - if(i < 0){ - var l = context.stack[context.stack.length-1] - l.splice(l.length + i, 0, e) - } else { - context.stack[context.stack.length-1].splice(i, 0, e) - } - }, - // b -- b e - 'pop': function(context){ - return context.stack[context.stack.length-1].pop() }, - // b -- b l - 'len': function(context){ - return context.stack[context.stack.length-1].length }, - // b c -- b - 'map': function(context){ - var c = context.stack.pop() - var b = context.stack[context.stack.length-1] - for(var i=0; i < b.length; i++){ - // exec block in a separate context... - var res = run({ - //stack: [b, i, b[i], c], - stack: [b[i], c], - code: ['exec'], - // NOTE: this can have side-effects on the context... - ns: context.ns - }).stack - var l = res.length - if(l == 0){ - b.splice(i, 1) - i-- - } else { - b.splice.apply(b, [i, 1].concat(res)) - i += l - 1 - } - } - }, - - - // object stuff... - '{}': function(){ return {} }, - - 'object?': function(context){ - var o = context.stack[context.stack.length - 1] - return o && o.constructor === Object - }, - - // set item... - // o k v -- o - 'item!': function(context){ - var v = context.stack.pop() - var k = context.stack.pop() - var o = context.stack[context.stack.length - 1] - - o[k] = v - }, - - // test item... - // o k -- o t - 'item?': function(context){ - return context.stack.pop() in context.stack[context.stack.length - 1] }, - - // get item... - // o k -- o v - 'item': function(context){ - var k = context.stack.pop() - return context.stack[context.stack.length - 1][k] }, - - // remove/pop item from object... - // o k -- o v - 'popitem': function(context){ - var k = context.stack.pop() - var o = context.stack[context.stack.length - 1] - - var v = o[k] - delete o[k] - - return v - }, - - // o -- k - 'keys': function(context){ - return Object.keys(context.stack.pop()) }, - - // make a prototype of b... - // a b -- b - // NOTE: if a is false, reset prototype... - 'proto!': function(context){ - var b = context.stack.pop() - var a = context.stack.pop() - b.__proto__ = a === false ? {}.__proto__ : a - return b - }, - - // o -- p - // XXX what should this be: - // {} getproto - 'proto': function(context){ - return context.stack.pop().__proto__ }, - - // -- o - 'ns': function(context){ - return context.ns }, - // o -- - 'ns!': function(context){ - context.ns = context.stack.pop() }, -} - - -// NOTE: hate how JS handles multi-line strings... -var BOOTSTRAP = -`------------------------------------------------------------------------------- - - [S]lang is a [s]imple and complete [S]tack [lang]uage. - - Slang was designed for three main reasons: - - a means to experiment with several aspects of language design, - - an educational tool, to illustrate several programming language - concepts in a simple, hands-on manner, - - fun! - - - -------------------------------------------------------------------------------- - - Slang Basics - ------------ - - The system consists of: - - Stack - - Code - - Namespace - - basic runtime - - { NAMESPACE } - ^ - | - [ .. STACK .. ] <-- runtime -- [ .. CODE .. ] - - - A namespace is a basic key/value store. - - The runtime "reads" entities from the code stream one by one and depending on - whether an entity exists in the namespace it is either pushed on the stack - or evaluated. - - The evaluated entity is traditionally called a "word" (function in non-stack - languages). The only thing that makes a word different from any other entity - is that it matches a key in the namespace, as mentioned above. - - In Slang evaluation is done simply by executing the value of the matched - key/value pair in the namespace. An over-simplified way to explain - evaluation is to say that the content of the value is pushed to the - code stream to be read right away, that\'s almost it, if we skip a - couple of details (see: _exec, exec and for details see: eval) - - The system contains two types of words: - - Host words -- defined by the host system, - - User words -- defined within the system (like this bootstrap code). - - Words may read and affect any of the three system parts: - - Stack - - Code - - Namespace - - Traditioannly, in stack languages words affect only the stack, this is - one of the motivations to implement Slang, that is, to experiment with - different ways to go with stack languages. - - - TODO: add topological comparison/diff - - - ------------------------------------------------------------------------------ - - Stack effect notation - --------------------- - - Traditionally, stack languages use a "stack effect" notation to document how - words affect the stack state, a kind of before-after transformation. here is - a basic example showing how the word "add" works: - - stack code - | 1 2 add - 1 | 2 add - 1 2 | add - 1 2 [add] (a) - 3 | (b) - - - Here the stack effect represents the difference between two states: the - moment when the word is "read" (a) and the stack state after it is - evaluated (b) and is written like this: - - ( a b -- c ) - - - But, due to the fact that in Slang all three of the stack, code and namespace - can be affected by words, we need an extended stack effect notation. to - include at least the second most common case, the "code effect". - To illustrate, here is an example of a word that has a simple code effect, - the "+": - - stack code - | 1 + 2 - 1 | + 2 - 1 [+] 2 (a) - 3 | (b) - - - Here we see that in addition to affecting the stack, 2 is "pulled" from the - code stream. To indicate this we will use "|" that splits the stack (left) - and code (right) states, and write the stack effect for the word "+" like - this: - - ( a | b -- c | ) - - - NOTE: this notation is currently used as a means to documenting words and is - not interpreted in any way. - - - -------------------------------------------------------------------------------- - - Blocks / Lists - -------------- - - Basic words for block manipulation: - - Get block length - - [ 1 2 3 ] len - -> [ 1 2 3 ] 3 - - - Pop element form block tail - - [ 1 2 3 ] pop - -> [ 1 2 ] 3 - - - Push element to block tail - - [ 1 2 3 ] 4 push - -> [ 1 2 3 4 ] - - - NOTE: all indexes can be negative values, these will indicate the - position relative to the tail, -1 being the last element. - - Get element at position (0) - - [ 1 2 3 ] -1 at - -> [ 1 2 3 ] 3 - - - Put element (123) at position (0) - - [ 1 2 3 ] 123 0 to - -> [ 123 2 3 ] - - - Put element (123) before position (0) - - [ 1 2 3 ] 123 0 before - -> [ 123 1 2 3 ] - - - Like before but puts the element after position - - [ 1 2 3 ] 123 0 after - -> [ 1 123 2 3 ] - - - Expand block to stack -- "block 2 stack" - - [ 1 2 3 ] b2s - -> 1 2 3 - - - Map a block/word to each element in a block - - [ 1 2 3 ] [ 1 add ] map - -> [ 2 3 4 ] - - the returned value (stack) of the input block is put into the result - block, this enables us to both remove (empty stack) and expand (more - than one value) the resulting list... - - [ 1 2 3 4 ] [ dup ] map - -> [ 1 1 2 2 3 3 4 4 ] - - [ 1 2 3 4 ] [ dup 2 gt ? [ ] else [ . ] ] map - -> [ 3 4 ] - - - this enables us to construct words like filter, which makes the code - in the last example more readable: - - [ 1 2 3 4 ] [ 2 gt ] filter - -> [ 3 4 ] - - Reduce enables us to take a list and "reduce" it to a single value... - - [ 1 2 3 4 ] \\add reduce - -> 10 - - -------------------------------------------------------------------------------- - - Objects and namespaces - ---------------------- - - Get the namespace object... - - ns -> namespace - - - Set attribute (key-value pair) on object... - - o x 123 item! - -> o - - Since almost all object words return the original object we can chain - object operations like this: - - Create a variable word o and p and set them to empty objects... - - ns - o {} item! - p {} item! - . - - Get attribute x value... - - o x item - -> 123 - - Test if attribute x exists... - - o x item? - -> true - - Get block of attribute idents... - - o keys - -> [ ... ] - - Get and remove an attribute value from o... - - o x popitem - -> 123 - - Set prototype of o to p - - o p proto! - -> o - - Get prototype of o - - o proto - -> p - - -------------------------------------------------------------------------------- -s2b drop -- cleanup after docs... -ns {} proto! ns! . -- keep new words in a seporate context... --- --- With that out of the way, let\'s start with the bootstrap... - --- prepare the basic syntax for defining words... -ns - -- Some shorthands... - . ( x -- ) - [ drop ] item! - rot2 ( .. x y -- x y .. ) - [ rot rot ] item! - tor2 ( x y .. -- .. x y ) - [ tor tor ] item! - - -- Friendly exec... - exec ( b -- ... ) - [ s2b pop _exec b2s ] item! - -- Create a word... - word! ( w b -- ) - [ rot2 ns tor2 item! . ] item! - -- Word definition... - -- syntax: :: - :: ( | w b -- | ) - [ \\word! \\exec 2 2 _swapN ] item! -. - - --- misc... - -:: true? ( a -- b ) [ not not true eq ] -:: false? ( a -- b ) [ not true? ] - --- we already have gt and eq, now let\'s define the rest... -:: ne ( a b -- c ) [ eq not ] -:: lt ( a b -- c ) [ swap gt ] -:: ge ( a b -- c ) [ lt not ] -:: le ( a b -- c ) [ gt not ] - -:: inc ( a -- b ) [ 1 add ] -:: dec ( a -- b ) [ 1 sub ] -:: ! ( a -- b ) [ [ dup 1 ne ] ? [ dup 1 sub ! mul ] ] - - - --- Stack/code manipulation... - -:: _swap ( x | y -- y | x ) [ 1 1 _swapN ] -:: _push ( x | -- | x ) [ 0 _swapN ] -:: _pull ( | x -- x | ) [ 0 swap _swapN ] - -:: eval ( c -- ... ) [ lex prep exec ] --- like exec but will run a block in current context... -:: b2c [ len rot b2s tor 0 _swapN ] - -:: swap2 ( a _ b -- b _ a ) [ swap rot swap tor swap ] -:: dup2 ( a b -- a b a b ) [ dup swap2 dup rot swap2 tor swap ] - --- this is here for devel use only -:: _clear ( ... -- ) [ s2b print drop ] -:: _stack_size ( -- l ) [ s2b len swap b2s tor ] - - - --- Flow control... - --- Classic conditional word: --- [ cond ] [ A ] [ B ] if --- --- A bit too "polish" in my view ;) -:: if ( cond a b -- ... ) [ rot rot exec true? tor and tor or exec ] - --- Ternary operator, this can take two forms: --- COND ? A --- COND ? A else B --- --- We will define this in stages, first the helpers: --- run then block and drop \'else B\' if it exists... -:: _run_then ( a x | -- ... | x ) - ( a else | b -- ... | ) - [ \\exec swap dup \\else eq [ (drop else) drop \\drop _swap 6 ] and - [ (run as-is) 1 _push 4 ] or - b2s 0 _swapN ] --- if \'else B\' exists, run it, else cleanup... -:: _run_else ( a | -- | a ) - ( b else | b -- ... | ) - [ drop dup \\else eq [ drop \\exec _swap 4 ] and - [ 1 _push 2 ] or - b2s 0 _swapN ] --- And now the main operator... --- NOTE: this may actually have one of three stack effects... -:: ? ( c | a -- | ) - ( c | a -- ... | ) - ( c | a else b -- ... | ) - [ exec [ _run_then 1 ] and [ swap _run_else 2 ] or b2s 2 _swapN ] - - - --- List/block 2\'nd gen stuff... - --- make a new block instance shorthand... -:: [] [ [ ] clone ] - --- insert element after index... -:: after ( b e i -- b ) [ - -- special case, when at end, need to push the alement after it... - dup [ -1 eq ] ? - [ . push ] - else - [ inc before ]] - --- NOTE: the "]]" in the last definition, it\'s a shorthand, it closes --- ALL the open blocks to this point. --- ...thus it can be used ONLY as the very last word in a set. - --- push element to tail of block... -:: push ( b e -- b ) [ swap len rot swap tor to ] - --- Replace a pattern (p) in block with value (v)... --- NOTE: this will replace ALL patterns... -:: replace ( l v p -- l ) [ - swap - [ . \\VALUE ] clone - swap 2 to - rot - -- XXX for some reason ? without else messes things up... - [ dup \\PATTERN eq ? VALUE_BLOCK else [ ] ] clone - swap 2 to - tor 5 to - map ] - --- Filter the block via a condition... --- --- the condition block must have the folowing stack effect: elem -- bool -:: filter ( b c -- b ) [ - -- prepare the condition... - [ dup \\TEST exec ] clone - swap TEST replace - -- prepare the template... - [ TEST ? [ ] else [ . ] ] clone - swap TEST replace - map ] - -:: reduce ( L b -- s ) [ - rot clone - -- empty list, reduction is null... - [ len 0 eq ] ? - [ . tor . null ] - -- reduction of list of len 1 is it\'s content, so just pop it... - else [ [ len 1 eq ] ? - [ tor . b2s ] - -- and now recursively reduce the elements till the list is 1 in length... - -- XXX ugly - else [ - pop rot pop rot - [] tor push tor push - -- get and run the block... - tor dup clone rot _exec - -- process the result... - pop rot . tor push tor - reduce ]] - --- Create a block containing a range of numbers form 0 to n-1... -:: range ( n -- b ) [ - -- initial state... - [ dup number? ] ? - [ [] swap ] - -- get first elem... - else - [ 0 at ] - -- we got to the end... - [ dup 0 eq ] ? - drop - -- dec push new and continue... - else - [ 1 sub 0 before range ]] - --- Sum up the elements of a block... -:: sum ( L -- s ) [ [ add ] reduce ] - - --- Meta-word examples (experimental)... - --- Here is an infix operator example... --- :: + ( a | b -- c | ) [ \\exec 2 0 _swapN \\exec \\add 2 1 _swapN ] --- now let\'s make a meta function to make things shorter... -:: infix: ( | op word -- | ) [ - [ - -- format the word definition... - -- NAME WORD -> :: NAME WORD - s2b \\:: -2 before b2s - - -- our template... - -- exec the left side... - [ \\exec 2 0 _swapN - -- exec the right side and arragne the args for WORD... - \\exec \\WORD 2 1 _swapN ] clone - -- get the WORD and insert it into the template above (opsition 8)... - swap WORD replace - - -- push to code / run - 3 0 _swapN - -- swap the arguments and the code to be executed... - ] \\exec 2 2 _swapN ] - --- Now making a word/2 an infix operator is trivial... --- NOTE: these are at this point stupid and do not support priorities... -infix: + add -infix: - sub -infix: * mul -infix: / div - --- these need more thought... -infix: == eq -infix: != ne -infix: > gt -infix: < lt -infix: <= le -infix: >= ge - --- experimental... -infix: = word! - - --- Prefix operation definition... --- Example: --- :: echo: ( | txt -- | ) [ \\_flip \\print _flip ] --- swap stack and code untill the block finishes and consumes it's arguments --- then swap them back... -:: prefix: ( | op word -- | ) [ - [ - -- format the word definition... - -- NAME WORD -> :: NAME WORD - s2b \\:: -2 before b2s - - -- the code template - [ \\_flip \\exec \\WORD _flip ] clone - swap WORD replace - 3 0 _swapN - ] \\exec 2 2 _swapN ] - - - --- Tests and examples... - --- Mandatory "hello word" word example... -:: hi ( -- ) [ "Hello World!" print drop ] - --- Create a block containg a range of numbers from f to t, inclusive... -:: range/2 ( f t -- b ) - [ dup2 swap sub swap . inc range swap [] swap push \\+ 0 before map ] - --- this will enable us to create ranges via 0 .. 4 -infix: .. range/2 - ---:: range/3 ( a n s -- b ) --- [ swap range swap [] swap push \\* 0 before map ] - --- Execute block in a context... --- synctx: context: -prefix: context: [ ns {} proto! ns! exec ns proto ns! ] - - -` - - -var STARTUP = [[], BOOTSTRAP, 'lex', 'prep', '_exec', 'drop'] - - -// build bootstrap... -var CONTEXT = { - stack: [], - code: STARTUP.slice(), - ns: NAMESPACE, - pre_ns: PRE_NAMESPACE, -} - - -// run bootstrap... -run(CONTEXT) - - -// convenience... -function _slang(code, context){ - context = context == null ? CONTEXT : context - - context.code = code - return run(context).stack -} - - -function slang(code, context){ - context = context == null ? CONTEXT : context - - if(typeof(code) == typeof('abc')){ - code = [ '\\', code, 'lex', 'prep', 'exec' ] - } else { - code = [ code, 'prep', 'exec' ] - } - - context.code = code - return run(context).stack -} - - - -/********************************************************** RSlang ***/ -/* -var RS_PRE_NAMESPACE = { - // XXX using the ";" here just for the experiment, in the real thing - // if this thing pans out, that is, use indent... (a-la make/Python) - // XXX this reads ahead at the moment, but it must read back... - ';': function(context){ - var line = [] - var code = context.code - var cur = code.splice(0, 1)[0] - while(cur != ';' && code.length > 0){ - line.push(cur) - cur = code.splice(0, 1)[0] - } - - context.code.splice.apply(context.code, [0, 0].concat(line.reverse())) - }, - - '[': PRE_NAMESPACE['['], - 'macro:': PRE_NAMESPACE['macro:'], -} - -RS_CONTEXT = { - stack: [], - code: STARTUP.slice(), - ns: NAMESPACE, - pre_ns: PRE_NAMESPACE, -} - -// NOTE: we use the traditional bootstrap for this... -run(RS_CONTEXT) - -RS_CONTEXT.pre_ns = RS_PRE_NAMESPACE - -function rslang(code, context){ - context = context == null ? RS_CONTEXT : context - - return slang(code, context) -} -//*/ - - - -/********************************************************************** -* vim:set ts=4 sw=4 spell : */ diff --git a/TeachersTimer/index.html b/TeachersTimer/index.html deleted file mode 100755 index 5ddc93d..0000000 --- a/TeachersTimer/index.html +++ /dev/null @@ -1,222 +0,0 @@ - - -Teacher's Timer - - - - - - - - -
-
+
- - - - - diff --git a/js-oop-constructors.jpg b/js-oop-constructors.jpg deleted file mode 100755 index d937d8213bc480dc73aa623b72af7ad5c20be64c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18909 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 diff --git a/js-oop.js b/js-oop.js deleted file mode 100755 index 8a2ac87..0000000 --- a/js-oop.js +++ /dev/null @@ -1,706 +0,0 @@ -/********************************************************************** -* -* The basics of JavaScript OOP -* -* -**********************************************************************/ - - - -/*********************************************************************/ -// -// The basic prototype inheritance -// ------------------------------- -// -// First we'll create a basic object a - - var a = { - x: 1, - y: 2, - } - -// Then we will create a new object using 'a' as a "base" - - var b = Object.create(a) - b.z = 3 - -// The object 'b' now has both access to it's own attributes ('z') and -// attributes of 'a' ('x' and 'y') - - b.x // -> 1 - b.z // -> 3 - -// What we see is that if the attribute is not found in the current -// object the next object is checked, and so on, this next object is -// called "prototype". -// These prototype chains can be of any length. -// Cycles in prototype chains are not allowed, see note further down for -// an example. -// -// Note that this works for reading (and mutating) attributes, but when -// writing or deleting we are affecting ONLY the local object and -// attributes explicitly defined in it, or its' "own" attributes. - - b.x = 321 - b.x // -> 321 - a.x // -> 1 - -// Notice also that a.x is no longer visible from 'b', this is called -// "shadowing", we say: a.x is shadowed by b.x, now let us delete 'x' -// from 'b' to reveal the shadowed a.x - - delete b.x - b.x // -> 1 - -// But, trying to delete .x from 'b' again will have no effect, this is -// because .x no longer exists in 'b' - - delete b.x - b.x // -> 1 - - -// Now back to the mechanism that makes all of this work... -// -// First we'll try couple of easy ways to see the local and non-local -// sets of attributes: - - // show local or "own" only attribute names (keys)... - Object.keys(b) // -> z - - // show all accessible keys... - for(var k in b){ console.log(k) } - // -> x, y, z - -// Another way to test if the attribute is own/local - - b.hasOwnProperty('z') // -> true - b.hasOwnProperty('x') // -> false - - -// What happens under the hood is very simple: b references it's -// "prototype" via the .__proto__ attribute: - - b.__proto__ === a // -> true - - -// We can read/set this special attribute just like any other attribute -// on most systems. -// -// NOTE: we did not see .__proto__ in the list of accessible attributes -// because it is a special attribute (property), it is implemented -// internally and is not enumerable. -// NOTE: cyclic prototype chains are actively not allowed, e.g. creating -// a chain like the following will fail: -// var a = {} -// var b = Object.creating(a) -// a.__proto__ = b -// -// -// Thus, we could define our own equivalent to Object.create(..) like -// this: - - function clone(from){ - var o = {} - o.__proto__ = from - return o - } - - var c = clone(b) - - -// Out of curiosity let's see if .__proto__ is defined on a basic object - - var x = {} - - x.__proto__ // -> {} - -// Turns out it is, and it points to Object's prototype - - x.__proto__ === Object.prototype - // -> true - -// We will discuss what this means and how we can use this in the next -// sections... -// -// As a side note, Object.prototype is the "root" most object in -// JavaScript and usually is "terminated" with null, i.e.: - - Object.prototype.__proto__ === null - -// We'll also need this a bit later... -// -// And can create an object with a null prototype like this: - - var raw_obj = Object.create(null) - var raw_obj = clone(null) - - // or manually... - var raw_obj = {} - raw_obj.__proto__ = null - -// These "raw" objects differ from normal objects in that they do not -// inherit any interface methods, defined in the Object, like the -// .hasOwnProperty(..) we used above, this can be useful in some cases. - - - -// The Constructor Mechanism -// ------------------------- -// -// JavaScript provides a second, complementary mechanism to inherit -// attributes, it resembles the class/object relationship in languages -// like C++ but this resemblance is on the surface only, as it still -// uses the same prototype mechanism as basis, as described above. -// -// We will start by creating a "constructor": - - function A(){ - this.x = 1 - this.y = 2 - } - -// Technically a constructor is just a function, what makes it a -// "constructor" is only how we use it... - - var a = new A() - - -// Some terminology: -// - in the above use-case 'A' is called a constructor, -// - the object returned by new is called an "instance" (in this case -// assigned to 'a'), -// - the attributes set by the constructor (x and y) are called -// "instance attributes" and are not shared (obviously) between -// different instances, rather they are "constructed" for each -// instance independently. -// -// -// Let's look in more detail at what 'new' does here: -// 1) creates an empty object -// 2) sets a bunch of attributes on it, we'll skim this part for now -// 3) passes the new object to the constructor via 'this' -// 4) after the constructor finishes, this object is returned -// -// We could write a simplified equivalent function: - - function construct(func){ - var obj = {} - func.apply(obj) - return obj - } - - var b = construct(A) - -// But at this point this all looks like all we did is move the attribute -// definition from a literal object notation into a constructor function, -// effectively adding complexity. -// And now instead of "inheriting" and reusing attributes we make a new -// set for each individual instance. -// So hat are we getting back from this? -// -// To answer this question we will need to look deeper under the hood, -// specifically at a couple of special attributes: - - // we saw this one before... - a.__proto__ // -> {} - - // this points back to the constructor... - a.constructor // -> [Function A] - - -// These are what makes this fun, lets write a more complete 'new' -// re-implementation: - - function construct(func, args){ - var obj = {} - - // set some special attributes... - obj.__proto__ = func.prototype - - // call the constructor... - var res = func.apply(obj, args) - - // handle the return value of the constructor... - if(res instanceof Object){ - return res - } - return obj - } - - var b = construct(A) - - -// There are two important things we added here: -// 1) we now explicitly use the .prototype attribute that we saw earlier -// 2) we return the resulting object in a more complicated way -// -// Each time a function is created in JavaScript it will get a new empty -// object assigned to it's .prototype attribute. -// On the function level, this is rarely used, but this object is very -// useful when the function is used as a constructor. -// -// As we can see from the code above, the resulting object's .__proto__ -// points to the constructor's .prototype, this means not-own the -// attributes accessed via that object are resolved to the prototype. -// In the default case this is true, but in general it's a bit more -// flexible, we'll see this in the next section. -// -// And the way we handle the return value makes it possible for the -// constructor to return a custom object rather than use the one -// provided in its "this" by new. -// -// -// So if we add stuff to the constructor's .prototype they should be -// accessible from the object - - // the following three lines actually add attributes to the same - // object... - A.prototype.k = 123 - a.constructor.prototype.l = 321 - a.__proto__.m = 333 - - // for illustration, we'll set some object own attributes - a.k = 'a!' - b.k = 'b!' - - a.k // -> 'a!' - a.l // -> 321 - a.m // -> 333 - - -// These values are accessible from all objects constructed by A since -// all of them point to A with both the .constructor and .__proto__ -// attributes - - b.k // -> 'b!' - b.l // -> 321 - b.m // -> 333 - - -// This works for any constructor, including built-in constructors and -// since name resolution happens in runtime all instances will get the -// new functionality live, as it is defined: - - // a "class method", like .keys(..) but return all available keys... - Object.allKeys = function(o){ - var res = [] - for(var k in o){ - res.push(k) - } - return res - } - // now make these into real methods we can use from any object... - Object.prototype.keys = function(){ return Object.keys(this) } - Object.prototype.allKeys = function(){ return Object.allKeys(this) } - - b.keys() // -> ['k'] - b.allKeys() // -> ['x', 'y', 'k', 'l', 'm'] - // NOTE: x and y are set in the A constructor - // above... - - - -// Inheritance chains -// ------------------ -// -// In both inheritance mechanisms, each step is checked via the same -// rules recursively, this makes it possible to build inheritance chains -// -// We will create a chain: -// -// c -> b -> a -// - - var a = {x: 1} - - var b = Object.create(a) - b.y = 2 - - var c = Object.create(b) - - - c.x // -> 1 - c.y // -> 2 - - -// Creating an inheritance chain via the constructor mechanism is a bit -// more involved, and there are multiple ways to do this... -// -// Here we will create a similar chain to the above for comparison: -// -// C -> B -> A -// - - function A(){} - - A.prototype.x = 1 - - - function B(){} - // NOTE: if this is done after an instance is created, that instances' - // .__proto__ will keep referencing the old prototype object. - // see the next constructor for a way around this... - B.prototype = Object.create(A.prototype) - // NOTE: we'll need to overwire this to B as the value inherited from - // A.prototype will obviously be A... - B.prototype.constructor = B - - B.prototype.y = 2 - - - function C(){} - // NOTE: this is safer than Object.create as it does not overwrite - // the original C.prototype and thus will affect all existing - // instances of C, if any were created before this point... - // NOTE: the C.prototype.constructor field is already set correctly - // here as we are not replacing the object created by the - // system... - C.prototype.__proto__ = B.prototype - - - var c = new C() - - c.x // -> 1 - c.y // -> 2 - - - -// Checking inheritance (instanceof) -// --------------------------------- -// -// An object is considered an instance of its' constructor and all other -// constructors in the inheritance chain. - - c instanceof C // -> true - c instanceof B // -> true - c instanceof A // -> true - c instanceof Object // -> true - - c instanceof function X(){} - // -> false - - -// This also works for our manually created objects - - var cc = construct(C) - - cc instanceof C - - -// But this will not work outside the constructor model, i.e. if the right -// parameter is not a function. - - var x = {} - var y = Object.create(x) - - try{ - // this will fail as x is not a function... - y instanceof x - } catch(e){ - console.log('error') - } - - -// Again to make this simpler to understand we will implement our own -// equivalent to instanceof: - - function isInstanceOf(obj, proto){ - return obj === Function && proto === Function ? true - : (isInstanceOf(proto, Function) - && (obj.__proto__ === proto.prototype ? true - // NOTE: the last in this chain is Object.prototype.__proto__ - // and it is null - : obj.__proto__ == null ? false - // go down the chain... - : isInstanceOf(obj.__proto__, proto))) - } - - - isInstanceOf(c, C) // -> true - isInstanceOf(c, B) // -> true - isInstanceOf(c, A) // -> true - isInstanceOf(c, Object) - // -> true - - isInstanceOf(c, function X(){}) - // -> false - - -// Also take note of the following cases: - - Object instanceof Function - // -> true - Function instanceof Object - // -> true - Object instanceof Object - // -> true - Function instanceof Function - // -> true - - -// Now, the fact that a function object is both a function and an object -// should be obvious: - - function f(){} - - f instanceof Function - // -> true - f instanceof Object - // -> true - - - -// Checking type (typeof) -// ---------------------- -// -// This section is mainly here for completeness and to address several -// gotcha's. -// -// What typeof returns in JavaScript is not too useful and sometimes -// even odd... - - typeof c // -> 'object' - -// This might differ from implementation to implementation but -// essentially the main thing typeof is useful for is distinguishing -// between objects and non-objects (numbers, strings, ...etc.) - - // non-objects - typeof 1 // -> 'number' - typeof Infinity // -> 'number' - typeof 'a' // -> 'string' - typeof undefined // -> 'undefined' - - // objects - typeof {} // -> 'object' - typeof [] // -> 'object' - - // the odd stuff... - typeof NaN // -> 'number' - typeof null // -> 'object' - typeof function(){} // -> 'function' - - -// NOTE: the "non-object" term is not entirely correct here, they can -// be called "frozen" objects in ES5 speak, but that is outside the -// scope of this document. - - - -// Methods and the value of 'this' -// ------------------------------- -// -// A "method" is simply an attribute that references a function. - - function f(){ - return this - } - - var o = { f: f } - -// Thus we call the attribute .f of object o a "method" of object o. -// -// -// 'this' is a reserved word and is available in the context of a function -// execution, not just in methods, but what value it references depends -// on how that function is called... -// 'this' is mostly useful and used in methods. -// -// A simple way to think about it is that 'this' always points to the -// "context" of the function call. -// -// There are three distinct cases here: -// - function call / implicit context -// - new call / implicit context -// - method call / explicit context -// -// -// 1) function call (implicit) -// In the first case the context is either global/window/module which -// ever is the root context in a given implementation or undefined in -// ES5 strict mode - - f() // -> window/global/module - - -// Strict mode example: -// - function strict_f(){ - 'use strict' - return this - } - - strict_f() // -> undefined - - -// 2) new call (implicit) -// Here as we have discussed before, 'this' is assigned a new object -// with some special attributes set. - - new f() // -> {} - - -// 3) method call (explicit) -// In the method call context this is set to the object from which the -// method is called, i.e. the object left of the '.' or [ ] attribute -// access operators... - - o.f() // -> o - o['f']() // -> o - - -// ...or an explicitly passed to .call(..) / .apply(..) function methods - - f.call(o) // -> o - f.apply(o) // -> o - - -// ES5 also defines a third way to make method calls: Function.bind which -// creates a new function where 'this' is bound to the supplied object - - var ff = f.bind(o) - ff() // -> o - - -// NOTE: all of the above 5 calls are the same. -// NOTE: the resulting from .bind(..) function will ignore subsequent -// .bind(..), .call(..) and .apply(..) method calls and 'this' will -// always be the original bound object. -// NOTE: the difference between strict and "quirks" modes is in the -// following: -// In quirks mode a function call is always done in the root -// context, it's like implicitly calling a method of the global -// object: -// f() === window.f() -// // -> true -// In strict mode these are two different things, a function call -// is done without a context ('this' is undefined) while calling -// the same function via the global object is essentially a method -// call, setting 'this' to what is to the left of the attribute -// access operator: -// strict_f() !== window.strict_f() -// // -> true - - - -// Common use-cases -// ---------------- -// -// Several common object construction patterns: -// -// * Literal objects... - - var LiteralObject = { - x: 1, - - method: function(a){ - return this.x * a - }, - } - - var o = Object.create(LiteralObject) - - -// Advantages: -// - simple and non-verbose -// - fully introspective -// - flexible and non-restrictive -// - supports basic inheritance -// -// Disadvantages: -// - needs a seporate manual instance construction stage (no -// constructor) -// - does not provide support for some of the base language -// infrastructure, like type and instance checking - - - -// * Constructor object... - - function ConstructorObject(){ - this.x = 1 - } - ConstructorObject.prototype.method = function(a){ - return this.x * a - } - - var o = new ConstructorObject() - -// Advantages: -// - flexible -// - fully introspective -// - supports language mechanisms for type and instance checking -// - supports inheritance -// -// Disadvantages: -// - more complicated than the literal notation -// - needs manual work to support inheritance, making it more even -// complicated -// - does not provide support for multiple inheritance - - - -// * Walled objects / Walled data - - function ObjectConstructor(){ - // private data and functions... - var x = 1 - - // the actual object defining both public data and methods... - return { - y: 2, - - method: function(a){ - // use the private and public data... - return this.y * x * a - }, - } - } - - var o = ObjectConstructor() - - -// Advantages: -// - supports hiding data from the user -// -// Disadvantages: -// - non-introspective -// - added complexity -// - makes inheritance and extending very complicated and in some -// cases impossible -// - copies code rather than reuses it -// - does not provide support for some of the base language -// infrastructure, like type and instance checking -// -// NOTE: mostly inspired by languages supporting internal strict data -// context restrictions (e.g. private data) from the C++ family, -// e.g. C++, Java, C# and friends... -// NOTE: this style is called "defensive" coding by some sources, -// including this one ;) -// NOTE: this approach has it's use-cases, mainly in code dealing with -// security, though general use of this pattern is not recommended -// as it adds lots of limitations and complexity without giving -// back any real benefits in the general case. - - - - -/*********************************************************************/ -// -// NOTE: several topics available in ES5 are intentionally excluded -// from this document, these include: -// - properties -// - freezing/sealing -// The general motivation for this is simple: they introduce -// complexity and restrictions without giving any real benefits -// in the common case. -// -// Cases where these features "might" be useful are: -// - language design / language extending -// - library code -// Neither of these is a common case and the use of these features -// for library code is debatable. -// -// -/********************************************************************** -* vim:set ts=4 sw=4 : */ diff --git a/jsssnake/jsssnake-generator-test.html b/jsssnake/jsssnake-generator-test.html deleted file mode 100755 index c8330ee..0000000 --- a/jsssnake/jsssnake-generator-test.html +++ /dev/null @@ -1,538 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
-
- diff --git a/jsssnake/jsssnake-test.html b/jsssnake/jsssnake-test.html deleted file mode 100755 index b14a48e..0000000 --- a/jsssnake/jsssnake-test.html +++ /dev/null @@ -1,522 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
-
- diff --git a/jsssnake/jsssnake-test.js b/jsssnake/jsssnake-test.js deleted file mode 100755 index ea07b45..0000000 --- a/jsssnake/jsssnake-test.js +++ /dev/null @@ -1,144 +0,0 @@ -function test(){ - var field = Field("field") - - // TODO UI... - - // setup a wall... - for(var i=8; i < 20; i++){ - field.Wall(field.cell(12, i)) - } - - // apple and apple creation on callback... - field.Apple(field.cell(10, 4)) - field.on_apple_eaten = function(){ - field.Apple(field.cell(13, 6)) - // remove the event handler... - field.on_apple_eaten = null - } - - // snake 0 script... - // test general control and being killed... - var s0 = field.Snake('black', field.cell(2, 2), 's', 3) - setTimeout(function(){s0.left()}, 3000) - setTimeout(function(){s0.right()}, 6000) - - // snake 1 script... - // test general controls and killing... - var s1 = field.Snake('blue', field.cell(6, 2), 's', 5) - setTimeout(function(){s1.right()}, 6000) - - // snake 2 script... - // test apple eating... - var s2 = field.Snake('silver', field.cell(7, 4), 'e', 2) - setTimeout(function(){s2.right()}, 5000) - setTimeout(function(){s2.right()}, 6000) - - // snake 3 script... - // test n/s wall traversal... - var s3 = field.Snake('green', field.cell(15, 2), 'n', 4) - setTimeout(function(){s3.right()}, 2000) - setTimeout(function(){s3.right()}, 3000) - - // snake 4 script... - // test l/r wall traversal... - var s4 = field.Snake('gold', field.cell(2, 15), 'w', 4) - setTimeout(function(){s4.right()}, 2500) - setTimeout(function(){s4.right()}, 3500) - setTimeout(function(){s4.right()}, 5000) - - // general game commands... - field.start(500) - setTimeout(function(){field.stop()}, 28000) - setTimeout(function(){field.reset()}, 30000) - - - // test the Game object... - setTimeout(function(){ - var game = JSSnakeGame(field) - - game.Wall() - game.Wall() - game.Wall() - game.Wall() - - game.Apple() - game.Apple() - game.Apple() - game.Apple() - }, 8000) - - // test for special cases... - setTimeout(function(){ - var game = JSSnakeGame(field) - - game.field.reset() - for(var i=0; i < game.field.cells.length; i++) - game.Wall(game.field.Cell(i), 3, 'n') - game.field.reset() - for(var i=0; i < game.field.cells.length; i++) - game.Wall(game.field.Cell(i), 3, 's') - game.field.reset() - for(var i=0; i < game.field.cells.length; i++) - game.Wall(game.field.Cell(i), 3, 'e') - game.field.reset() - for(var i=0; i < game.field.cells.length; i++) - game.Wall(game.field.Cell(i), 3, 'w') - }, 21000) - - setTimeout(function(){ - var game = JSSnakeGame(field) - - game.field.reset() - for(var i=0; i < game.field.cells.length; i++) - game.Apple(game.field.Cell(i)) - }, 22000) - - setTimeout(function(){ - setTimeout(function(){ - var game = JSSnakeGame(Field("field")) - game.field.reset() - game.levels.sand() - }, 100) - - setTimeout(function(){ - var game = JSSnakeGame(Field("field")) - game.field.reset() - game.levels.walls() - }, 2000) - - setTimeout(function(){ - var game = JSSnakeGame(Field("field")) - game.field.reset() - game.levels.dashed(20, 2) - }, 4000) - - setTimeout(function(){ - var game = JSSnakeGame(Field("field")) - game.field.reset() - game.levels.dashed(15) - }, 6000) - - setTimeout(function(){ - var game = JSSnakeGame(Field("field")) - game.field.reset() - game.levels.dashed(5, 18) - }, 8000) - - - setTimeout(function(){ - var game = JSSnakeGame(Field("field")) - game.field.reset() - setInterval(function(){ - game.field.reset() - game.levels.walls() - - var APPLES = 4 - - for(var i=0; i < APPLES; i++) - game.Apple() - }, 1000) - }, 10000) - }, 24000) -} - -// vim:set ts=4 sw=4 spell : diff --git a/jsssnake/jsssnake.html b/jsssnake/jsssnake.html deleted file mode 100755 index 209e08c..0000000 --- a/jsssnake/jsssnake.html +++ /dev/null @@ -1,536 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
-
- diff --git a/jsssnake/jsssnake.js b/jsssnake/jsssnake.js deleted file mode 100755 index 81e743e..0000000 --- a/jsssnake/jsssnake.js +++ /dev/null @@ -1,423 +0,0 @@ -var DEBUG = true - -function log(text){ - document.getElementById('log').innerHTML += text + '
' -} - -var Field = function(field_id){ - return ({ - // XXX HACK: get this from CSS... - APPLE_COLOR: 'red', - WALL_COLOR: 'gray', - FIELD_COLOR: 'white', - TICK: 200, - - // interface... - init: function(field_id, on_kill, on_apple_eaten){ - this.field = document.getElementById(field_id) - - // this depends on topology... - // NOTE: we consider that the field may not change during operation... - this.cells = this.field.getElementsByTagName("td") - this.height = this.field.getElementsByTagName("tr").length - - this.cell_count = this.cells.length - this.width = this.cell_count / this.height - - this.on_kill = on_kill - this.on_apple_eaten = on_apple_eaten - - this._timer = null - - this.reset() - - // rotation tables... - this._cw = { - 'n': 'e', - 's': 'w', - 'e': 's', - 'w': 'n' - } - this._ccw = { - 'n': 'w', - 's': 'e', - 'e': 'n', - 'w': 's' - } - - return this - }, - // setup/reset the field to it's original state. - reset: function(){ - this._snakes = {} - this._tick = 0 - this.stop() - for(var i=0; i < this.cells.length; i++){ - var cell = this.Cell(i) - cell.o.style.backgroundColor = this.FIELD_COLOR - } - }, - - // do a single step... - step: function(){ - var cells = this.cells - - for(var i=0; i < cells.length; i++){ - var cell = this.Cell(i) - // identify the object... - if(this.is_snake(cell)){ - this.Snake(cell.o.style.backgroundColor, cell).step() - } - } - this._tick += 1 - }, - start: function(tick){ - var that = this - if(tick === null){ - tick = this.TICK - } - if(this._timer === null){ - this._timer = setInterval(function(){that.step()}, tick) - } - }, - stop: function(){ - if(this._timer === null){ - return - } - clearInterval(this._timer) - this._timer = null - }, - - // get a cell helper... - Cell: function(n){ - var that = this - var cells = this.cells - return ({ - // NOTE: this will be null if a cell does not exist. - o: cells[n], - index: n, - // NOTE: these are cyclic... - n: function(){ - var t = n - that.width - if(t < 0) - t = that.cells.length + t - return that.Cell(t) - }, - s: function(){ - var t = n + that.width - if(t > that.cells.length-1) - t = t - that.cells.length - return that.Cell(t) - }, - e: function(){ - var t = n + 1 - if(Math.floor(t/that.width) > Math.floor(n/that.width)) - t = t - that.width - return that.Cell(t) - }, - w: function(){ - var t = n - 1 - if(Math.floor(t/that.width) < Math.floor(n/that.width)) - t = t + that.width - return that.Cell(t) - } - }) - }, - // get a cell by it's coordinates... - cell: function(x, y){ - return this.Cell(x + (y-1) * this.width) - }, - - // add a snake to the field... - // XXX BUG: size of 1 makes the snake endless... - Snake: function(color, cell, direction, size){ - var that = this - - // draw the snake if it does not exist... - if(this._snakes[color] == null){ - cell.o.style.backgroundColor = color - cell.o.age = size - this._snakes[color] = { - 'direction':direction, - 'size': size - } - } - - // NOTE: the only things this uses from the above scope is color and that. - // NOTE: color is the onlu thing that can't change in a snake. - return ({ - // XXX BUG: the last cell of a dead snake lives an extra tick... - kill: function(){ - // this will disable moving and advancing the snake... - that._snakes[color].size = 0 - if(that.on_kill != null){ - that.on_kill(that) - } - }, - step: function(){ - var direction = that._snakes[color].direction - var size = that._snakes[color].size - var target = cell[direction]() - - // skip a cell if it's already handled at this step. - if(cell.o.moved_at == that._tick){ - return - } - - // do this only for the head... - if(parseInt(cell.o.age) == size){ - // handle field bounds... - if(target.o == null){ - alert('out of bounds!') - return - } - // kill conditions: walls and other snakes... - if(that.is_snake(target) || that.is_wall(target)){ - // XXX move this to a separate action - this.kill() - return - } - // apple... - if(that.is_apple(target)){ - // grow the snake by one... - // XXX move this to a separate action - that._snakes[color].size += 1 - size = that._snakes[color].size - if(that.on_apple_eaten != null){ - that.on_apple_eaten(that) - } - } - // all clear, do the move... - target.o.style.backgroundColor = color - target.o.age = size - target.o.moved_at = that._tick - cell.o.age = size - 1 - - } else { - if(cell.o.age <= 1) { - cell.o.style.backgroundColor = that.FIELD_COLOR - } - cell.o.age = parseInt(cell.o.age) - 1 - } - }, - - // user interface... - left: function(){ - that._snakes[color].direction = that._ccw[that._snakes[color].direction] - }, - right: function(){ - that._snakes[color].direction = that._cw[that._snakes[color].direction] - } - }) - }, - is_snake: function(cell){ - var snakes = this._snakes - var color = cell.o.style.backgroundColor - - for(var c in snakes){ - if(c == color) - return true - } - return false - }, - - Apple: function(cell){ - cell.o.style.backgroundColor = this.APPLE_COLOR - return cell - }, - is_apple: function(cell){ - return cell.o.style.backgroundColor == this.APPLE_COLOR - }, - - Wall: function(cell){ - cell.o.style.backgroundColor = this.WALL_COLOR - return cell - }, - is_wall: function(cell){ - return cell.o.style.backgroundColor == this.WALL_COLOR - }, - - is_empty: function(cell){ - return cell.o.style.backgroundColor == this.FIELD_COLOR - } - }).init(field_id) -} - -// this defines the basic game logic and controls the rules and levels... -/* - NOTE: it is recommended to create game objects in the folowing order: - 1) walls - 2) apples - 3) players - */ -function JSSnakeGame(field){ - - var game = { - field: field, - TICK: 300, - - // this enables snakes of the same colors... - SIMILAR_COLORS: false, - used_colors: function(){ - // this is a workaround the inability to directly create an object - // with field names not a literal identifier or string... - var res = {} - res[field.FIELD_COLOR] = true - res[field.WALL_COLOR] = true - res[field.APPLE_COLOR] = true - return res - }(), - - // utility methods... - _random_empty_cell: function(){ - // NOTE: if we are really unlucky, this will take - // really long, worse, if we are infinitely unlucky - // this will take an infinite amount of time... (almost) - var field = this.field - var i = field.cells.length-1 - var l = i - while(true){ - var c = field.Cell(Math.round(Math.random()*l)) - if(field.is_empty(c)) - return c - i-- - if(i == 0) - return null - } - }, - // key handler... - // functions: - // - control snake - dispatch player-specific keys to player-specific snake - // - create player - two unused keys pressed within timeout, random color - // - // modes: - // - player add - // - game control - // - // NOTE: modes can intersect... - // NOTE: modes are game state dependant... - key_time_frame: 0.5, - pending_key: null, - - _keyHandler: function(evt){ - var name, color - var key = window.event ? event.keyCode : evt.keyCode - - // find a target registered for key... - // XXX - - // no one is registered... - // if wait time set and is not exceeded create a player and register keys - if(!this.pending_key || Date().getTime() - this.pending_key['time'] > this.key_time_frame ){ - // if no wait time is set, set it and remember the key... - this.pending_key = {time: Date().getTime(), key: key} - } else { - // get name... - // XXX - // get color... - // XXX - this.Player(name, this.pending_key['key'], key, color) - this.pending_key = null - } - return true - }, - // create a new player... - // NOTE: direction and position are optional... - // XXX BUG: players should not get created facing a wall directly... - Player: function(name, ccw_button, cw_button, color, cell, direction, size){ - if(!this.SIMILAR_COLORS && this.used_colors[color] == true){ - // error: that the color is already used... - return - } - // register controls... - // XXX - - if(direction == null){ - direction = ['n', 's', 'e', 'w'][Math.round(Math.random()*3)] - } - if(cell === null){ - cell = this._random_empty_cell() - if(cell === null) - return - } - // create a snake... - this.used_colors[color] = true - return this.field.Snake(color, cell, direction, size) - }, - // NOTE: position is optional... - Apple: function(cell){ - // place an apple at a random and not occupied position... - var c = cell? cell: this._random_empty_cell() - if(c === null) - return - return this.field.Apple(c) - - }, - // NOTE: all arguments are optional... - Wall: function(cell, len, direction){ - // generate random data for arguments that are not given... - if(cell == null){ - cell = this._random_empty_cell() - if(cell === null) - return - } - if(direction == null){ - direction = ['n', 's', 'e', 'w'][Math.round(Math.random()*3)] - } - if(len == null){ - if(direction == 'n' || direction == 's') - var max = this.field.height - else - var max = this.field.width - len = Math.round(Math.random()*(max-1)) - } - // place a wall... - for(var i=0; i < len; i++){ - field.Wall(cell) - cell = cell[direction]() - } - }, - - // level generators and helpers... - levels: { - dotted: function(n){ - for(var i=0; i < n; i++) - game.Wall(null, 1, null) - }, - dashed: function(n, length){ - if(length == null) - length = 3 - for(var i=0; i < n; i++) - game.Wall(null, length, null) - }, - // specific level styles... - sand: function(){ - this.dotted(Math.round(game.field.cells.length/20)) - }, - walls: function(){ - this.dashed( - Math.round(game.field.cells.length/90), - Math.min( - game.field.width, - game.field.height)-2) - } - }, - - start: function(){ - // start the game... - field.start(this.TICK) - }, - stop: function(){ - field.stop() - } - } - - field.on_apple_eaten = function(){game.Apple()} - field.on_kill = function(snake){game.used_colors[snake.color] = false} - - //document.onkeyup = function(evt){return game._keyHandler(evt)} - - return game -} - -// vim:set ts=4 sw=4 spell : diff --git a/simplesnake/manifest.json b/simplesnake/manifest.json deleted file mode 100644 index 833e5bc..0000000 --- a/simplesnake/manifest.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "short_name": "SimpleSnake", - "name": "SimpleSnake", - "display": "standalone", - "orientation": "landscape", - "Theme_color": "white" -} diff --git a/simplesnake/simplesnake.appcache b/simplesnake/simplesnake.appcache deleted file mode 100755 index 3c41518..0000000 --- a/simplesnake/simplesnake.appcache +++ /dev/null @@ -1,12 +0,0 @@ -CACHE MANIFEST -# Timestamp: 20170425215205 - -CACHE: -simplesnake.html -simplesnake.css -simplesnake.js -manifest.json - -NETWORK: -* - diff --git a/simplesnake/simplesnake.css b/simplesnake/simplesnake.css deleted file mode 100755 index 013f755..0000000 --- a/simplesnake/simplesnake.css +++ /dev/null @@ -1,165 +0,0 @@ -/**************************************************** Hints screen ***/ - -body.hints:before, -body.hints:after { - display: block; - position: absolute; - left: 0; - top: 12.5%; - bottom: 12.5%; - z-index: 100; -} -body.hints:before { - content: ""; - width: 100%; - border-top: dotted 0.3vmin rgba(255, 0, 0, 0.5); - border-bottom: dotted 0.3vmin rgba(255, 0, 0, 0.5); -} -body.hints:after { - content: "↺↻"; - width: 37vmin; - left: auto; - right: 50%; - - overflow: visible; - border-right: dotted 0.3vmin rgba(255, 0, 0, 0.5); - - color: rgba(255, 0, 0, 0.5); - - font-size: 15vmin; - line-height: 70vh; - white-space: pre; - letter-spacing: 38vw; -} -.hints .simplesnake { - opacity: 0.2; -} -.hints .title:before, -.hints .title:after { - position: absolute; - display: block; - text-align: center; - width: 100vw; - color: red; - font-size: 5vh; - line-height: 10vh; - z-index: 100; - opacity: 0.6; -} -.hints .title:before { - content: "New level"; -} -.hints .title:after { - bottom: 0; - content: "Pause game"; -} -.hints .title { - display: block; -} -.hints .title h1:after { - content: "Touch control hints..."; - display: block; - position: relative; - font-size: 4vmin; - font-weight: normal; - font-style: italic; - text-shadow: 3pt 3pt 10pt rgba(0,0,0,0.2); - opacity: 0.4; -} - - - -/*********************************************************** Title ***/ - -.title { - display: none; - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - text-align: center; - z-index: 500; - color: rgba(255,0,0,0.7); -} -.title h1 { - position: relative; - display: block; - top: 50%; - font-size: 10vmin; - margin-top: -22vmin; - font-weight: bolder; - text-shadow: 3pt 3pt 15pt rgba(0,0,0,0.2); -} -.title h1 sup { - font-weight: normal; - font-size: 5vh; -} - - - -/********************************************************* General ***/ - -body { - background-color: rgb(253, 253, 253); - font-family: sans-serif; - overflow: hidden; -} - - - -/************************************************************ Game ***/ - -.simplesnake .field { - position: absolute; - left: 50vw; - top: 50vh; - margin-left: -45vmin; - margin-top: -45vmin; - - width: 90vmin; - height: 90vmin; - border: solid 1px silver; -} - -/* show score... */ -.simplesnake[score]:after { - position: absolute; - display: block; - text-align: center; - width: 100vw; - color: gray; - font-size: 2vh; - line-height: 3vh; - bottom: 1vh; - - content: "Top score: " attr(snake) ": " attr(score) " " attr(state); - - opacity: 0.9; -} - -.simplesnake .wall { - background-color: silver; -} -.simplesnake .apple { - position: relative; - background-color: red; -} - -body:not(.hints) .simplesnake.paused:before { - content: "Paused..."; - position: absolute; - display: block; - width: 100%; - top: 50%; - margin-top: -10vmin; - font-size: 10vmin; - font-weight: bolder; - text-align: center; - color: rgba(0, 0, 0, 0.2); - z-index: 100; -} - - - -/*********************************************************************/ diff --git a/simplesnake/simplesnake.html b/simplesnake/simplesnake.html deleted file mode 100644 index 27c58ca..0000000 --- a/simplesnake/simplesnake.html +++ /dev/null @@ -1,22 +0,0 @@ - - - -Simple Snake - - - - - - - - - - - -

SimpleSnake

- -
- - - - diff --git a/simplesnake/simplesnake.js b/simplesnake/simplesnake.js deleted file mode 100755 index 6f109ee..0000000 --- a/simplesnake/simplesnake.js +++ /dev/null @@ -1,556 +0,0 @@ -/********************************************************************** -* -* Simple Snake -* -* This code is designed to illustrate the non-intuitive approach to an -* implementation, building a snake game as a cellular automaton rather -* than the more obvious, set of entities (OOP) or a number of sets -* of procedures and data structures, directly emulating the "tactile" -* perception of the game, i.e. independent field, snakes, walls, apples -* and their interactions. -* -* In this approach there are no entities, no snakes, no apples, no -* walls, just a set of cells in a field and cell behaviours per game -* step: -* - empty cells, apples and walls just sit there -* - "snake" cells: -* - decrement age -* - if age is 0 clear cell -* - if cell has direction (i.e. snake head) -* - if target cell is red (apple) increment age -* - color new cell in direction: -* - set age on to current age + 1 -* - set direction to current -* - clear direction -* -* NOTE: that in the above description some details are omitted for -* clarity... -* -* -* This code is structured in a scalable and introspective way: -* - Snake object is reusable as a prototype enabling multiple games -* to run at the same time -* - Snake implements an open external control scheme, i.e. it does not -* impose a specific way to implementing the way to control the game -* - Simple (but not trivial) code and code structure -* - Introspective: no hidden/masked state or functionality -* - No external dependencies -* -* -* Goals: -* - Show that the "intuitive" is not the only approach or is not -* necessarily the simplest... -* - Show one approach to a scalable yet simple architecture -* - Illustrate several programming patterns and approaches: -* - concatinative -* - see how Snake methods are implemented and how they are used -* in setup(..)... -* - see Snake.call(..) and Snake.apply(..) methods and how they -* enable extending the code inline... -* - meta-programming -* see: makeEvent(..) -* - event-oriented-programming -* see Snake events and how they are used in setup(..) to extend -* the basic game logic... -* - Show the use of several HTML5/CSS3 features including appCache, -* touch events and keyboard events and handling... -* -* -* -**********************************************************************/ - -var VERSION = '2.0' - - - -/*********************************************************************/ - -function makeEvent(handler_attr){ - return function(func){ - if(func === null){ - delete this[handler_attr] - - } else if(func instanceof Function){ - var handlers = this[handler_attr] = this[handler_attr] || [] - handlers.push(func) - - } else { - var that = this - var args = [].slice.call(arguments) - this[handler_attr] - && this[handler_attr] - .forEach(function(handler){ handler.apply(that, args) }) - } - return this - } -} - -var Snake = { - config: { - field_size: 32, - interval: 150, - }, - - _field: null, - _cells: null, - players: null, - field_size: null, - - get random_point(){ - var cells = this._cells - var l = cells.length - var w = this.field_size.width - - do { - var i = Math.floor(Math.random() * l) - } while(cells[i].classList.length > 0) - - return { - x: i%w, - y: Math.floor(i/w), - } - }, - get random_direction(){ - return ('nesw')[Math.floor(Math.random() * 4)] }, - - // utils... - call: function(func){ - return func.apply(this, [].slice.call(arguments, 1)) }, - apply: function(func, args){ - return func.apply(this, args) }, - normalize_point: function(point){ - point = point || {} - var w = this.field_size.width - var x = point.x % w - x = x < 0 ? (x + w) : x - var h = this.field_size.height - var y = point.y % h - y = y < 0 ? (y + h) : y - return { x: x, y: y } - }, - - // system... - setup: function(field, size, interval){ - this.config.field_size = size || this.config.field_size - this.config.interval = interval || this.config.interval - field = field || this._field - field = this._field = typeof(field) == typeof('str') ? document.querySelector(field) - : field - this._make_field() - this._cells = [].slice.call(field.querySelectorAll('td')) - this.field_size = { - width: field.querySelector('tr').querySelectorAll('td').length, - height: field.querySelectorAll('tr').length, - } - this.players = {} - return this - .appleEaten(null) - .snakeKilled(null) - }, - _make_field: function(w){ - var l = [] - l.length = w || this.config.field_size - l.fill('') - this._field.innerHTML = - `\n${ - l.map(function(){ - return ` ${ l.join('') } ` - }).join('\n') - }\n
` - }, - _tick: function(){ - var that = this - var l = this._cells.length - var w = this.field_size.width - var h = this.field_size.height - var tick = this.__tick = (this.__tick + 1 || 0) - var directions = 'neswn' - - this._cells.forEach(function(cell, i){ - var color = cell.style.backgroundColor - - // skip cells we touched on this tick... - if(cell.tick == tick){ - return - } - - // snake... - if(cell.age != null){ - // handle cell age... - if(cell.age == 0){ - delete cell.age - cell.classList.remove('snake') - cell.style.backgroundColor = '' - - } else { - cell.age -= 1 - } - - // snake head -> move... - var direction = cell.direction - if(directions.indexOf(direction) >= 0){ - // turn... - if(that.players[color] != ''){ - var turn = that.players[color] || '' - var j = turn == 'left' ? directions.indexOf(direction) - 1 - : directions.indexOf(direction) + 1 - j = j < 0 ? 3 : j - direction = directions[j] - that.players[color] = '' - } - - // get next cell index... - var next = - direction == 'n' ? - (i < w ? l - w + i : i - w) - : direction == 's' ? - (i > (l-w-1) ? i - (l-w) : i + w) - : direction == 'e' ? - ((i+1)%w == 0 ? i - (w-1) : i + 1) - : (i%w == 0 ? i + (w-1) : i - 1) - next = that._cells[next] - - var age = cell.age - var move = false - - // special case: other snake's head -> kill both... - if(next.direction){ - var other = next.style.backgroundColor - next.classList.remove('snake') - next.style.backgroundColor = '' - // NOTE: we are not deleteing .direction here as - // we can have upto 4 snakes colliding... - next.direction = '' - that.snakeKilled(other, next.age+1) - that.snakeKilled(color, age+2) - delete next.age - - // apple -> increment age... - } else if(next.classList.contains('apple')){ - age += 1 - move = true - next.classList.remove('apple') - that.appleEaten(color, age+2) - - // empty -> just move... - } else if(next.classList.length == 0){ - move = true - - // other -> kill... - } else { - that.snakeKilled(color, age+2) - } - - // do the move... - if(move){ - next.tick = tick - next.style.backgroundColor = color - next.classList.add('snake') - next.age = age + 1 - next.direction = direction - } - - delete cell.direction - } - } - cell.tick = tick - }) - this.tick(tick) - }, - - // constructors... - snake: function(color, age, point, direction){ - point = this.normalize_point(point || this.random_point) - - var head = this._cells[point.x + point.y * this.field_size.width] - head.style.backgroundColor = color - head.classList.add('snake') - head.direction = direction || this.random_direction - head.age = (age || 5) - 1 - this.players[color] = '' - - return this - .snakeBorn(color) - }, - apple: function(point){ - point = this.normalize_point(point || this.random_point) - var c = this._cells[point.x + point.y * this.field_size.width] - c.classList.add('apple') - c.style.backgroundColor = '' - return this - }, - wall: function(point, direction, length){ - direction = direction || this.random_direction - point = this.normalize_point(point || this.random_point) - var x = point.x - var y = point.y - length = length || Math.random() * this.field_size.width - - while(length > 0){ - var c = this._cells[x + y * this.field_size.width] - c.classList.add('wall') - c.style.backgroundColor = '' - - x += direction == 'e' ? 1 - : direction == 'w' ? -1 - : 0 - x = x < 0 ? this.field_size.width + x - : x % this.field_size.width - y += direction == 'n' ? -1 - : direction == 's' ? 1 - : 0 - y = y < 0 ? this.field_size.height + y - : y % this.field_size.height - length -= 1 - } - - return this - }, - level: function(level){ - var that = this - level.forEach(function(wall){ - that.wall.apply(that, wall) }) - return this - }, - - // events... - snakeKilled: makeEvent('__killHandlers'), - snakeBorn: makeEvent('__birthHandlers'), - appleEaten: makeEvent('__appleEatenHandlers'), - tick: makeEvent('__tickHandlers'), - gameStarted: makeEvent('__startHandlers'), - gameStopped: makeEvent('__stopHandlers'), - - // actions... - start: function(t){ - this.__timer = this.__timer - || setInterval(this._tick.bind(this), t || this.config.interval || 200) - // reset player control actions... - var that = this - Object.keys(this.players) - .forEach(function(k){ that.players[k] = '' }) - return this - .tick() - .gameStarted() - }, - stop: function(){ - clearInterval(this.__timer) - delete this.__timer - delete this.__tick - return this - .gameStopped() - }, - pause: function(){ - return this.__timer ? this.stop() : this.start() }, - left: function(color){ - this.players[color || Object.keys(this.players)[0]] = 'left' - return this - }, - right: function(color){ - this.players[color || Object.keys(this.players)[0]] = 'right' - return this - }, -} - - - -/*********************************************************************/ -// control event handlers... - -var KEY_CONFIG = { - ' ': ['pause'], - n: setup, - ArrowLeft: ['left'], - ArrowRight: ['right'], - // IE compatibility... - Left: ['left'], - Right: ['right'], - '?': function(){ - this - .stop() - .call(showHints) }, -} -function makeKeyboardHandler(snake){ - return function(event){ - clearHints() - var action = KEY_CONFIG[event.key] - action - && (action instanceof Function ? - action.call(snake) - : action[0] in snake ? - snake[action[0]].apply(snake, action.slice(1)) - : null) }} - -var __DEBOUNCE = false -var __DEBOUNCE_TIMEOUT = 100 -function makeTapHandler(snake){ - return function(event){ - // prevent clicks and touches from triggering the same action - // twice -- only handle the first one within timeout... - // NOTE: this should not affect events of the same type... - if(__DEBOUNCE && event.type != __DEBOUNCE){ return } - __DEBOUNCE = event.type - setTimeout(function(){ __DEBOUNCE = false }, __DEBOUNCE_TIMEOUT) - - clearHints() - // top of screen (1/8)... - ;(event.clientY || event.changedTouches[0].pageY) <= (window.innerHeight / 8) ? - setup() - // bottom of screen 1/8... - : (event.clientY || event.changedTouches[0].pageY) >= (window.innerHeight / 8)*7 ? - Snake.pause() - // left/right of screen... - : (event.clientX || event.changedTouches[0].pageX) <= (window.innerWidth / 2) ? - Snake.left() - : Snake.right() }} - - -//--------------------------------------------------------------------- -// misc stuff... - -function showHints(){ - document.body.classList.add('hints') } -function clearHints(){ - document.body.classList.remove('hints') } -function digitizeBackground(snake, walls){ - snake._cells.forEach(function(c){ - var v = Math.floor(Math.random() * 6) - // bg cell... - c.classList.length == 0 ? - (c.style.backgroundColor = - `rgb(${255 - v}, ${255 - v}, ${255 - v})`) - // wall... - : walls && c.classList.contains('wall') ? - (c.style.backgroundColor = - `rgb(${220 - v*2}, ${220 - v*2}, ${220 - v*2})`) - // skip the rest... - : null }) - return snake -} - - -//--------------------------------------------------------------------- - -var __CACHE_UPDATE_CHECK = 5*60*1000 -var __HANDLER_SET = false -function setup(snake, timer, size){ - snake = snake || Snake - - // levels... - var A = Math.round((size || snake.config.field_size)/8) - var Level = { - W3: [ - [null, null, A*6], - [null, null, A*6], - [null, null, A*6], - ], - Halves: [ - [null, null, A*8], - ], - Quarters: [ - [null, 's', A*8], - [null, 'e', A*8], - ], - Random3: [[], [], []], - - get random(){ - var l = Object.keys(this) - .filter(function(e){ return e != 'random' }) - do { - var level = this[l[ Math.round(Math.random()*l.length) ]] - } while(!(level instanceof Array)) - return level - }, - } - - function showScore(color, age){ - score = snake.__top_score = - (!snake.__top_score || snake.__top_score.score < age) ? - { - color: color || '', - score: age || 0, - } - : snake.__top_score - snake._field.setAttribute('score', score.score) - snake._field.setAttribute('snake', score.color) - snake._field.setAttribute('state', ( - score.score == age && score.color == color) ? '(current)' : '') - } - - // setup event handlers (only once)... - if(!__HANDLER_SET){ - document.querySelectorAll('.version') - .forEach(function(e){ e.innerHTML = VERSION }) - - // control handlers... - document.addEventListener('keydown', makeKeyboardHandler(snake)) - document.addEventListener('touchstart', makeTapHandler(snake)) - //document.addEventListener('mousedown', makeTapHandler(snake)) - - // cache updater... - var appCache = window.applicationCache - if(appCache - && appCache.status != appCache.UNCACHED){ - appCache.addEventListener('updateready', function(){ - if(appCache.status == appCache.UPDATEREADY){ - console.log('CACHE: new version available...') - appCache.swapCache() - - confirm('New version ready, reload?') - && location.reload() - } - }) - setInterval(function(){ appCache.update() }, __CACHE_UPDATE_CHECK) - } - - __HANDLER_SET = true - } - - // setup the game... - return snake - // prepare the field/game... - .setup('.simplesnake', size, timer) - .call(digitizeBackground, snake) - .call(function(){ - this.__snake_apples = [] - return this - }) - - // load level... - .level(Level.random) - - // game events / meta game rules... - // reconstruct eaten apples... - .appleEaten(function(color, age){ - this.apple() - showScore(color, age) - }) - // one apple per snake... - .snakeBorn(function(color){ - this.__snake_apples.indexOf(color) < 0 - && this.apple() - && this.__snake_apples.push(color) }) - // reconstruct snakes and pause game... - // XXX for multiplayer reconstruct the snake on timeout and do - // not pause... - .snakeKilled(function(color, age){ - this - .pause() - .snake(color, 3) - showScore(color, 3) - }) - // indicate game state... - .gameStarted(function(){ - this._field.classList.remove('paused') }) - .gameStopped(function(){ - this._field.classList.add('paused') }) - - // game eleemnts... - .apple() - .snake('blue', 3) -} - - - -/********************************************************************** -* vim:set ts=4 sw=4 spell : */