summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--js/lost.html2
-rw-r--r--js/lost.js296
2 files changed, 256 insertions, 42 deletions
diff --git a/js/lost.html b/js/lost.html
index 38f246e..820b1fc 100644
--- a/js/lost.html
+++ b/js/lost.html
@@ -1,5 +1,7 @@
1<!DOCTYPE html>
1<html> 2<html>
2 <head> 3 <head>
4 <title>Lost</title>
3 </head> 5 </head>
4 <body> 6 <body>
5 <script src="lost.js"></script> 7 <script src="lost.js"></script>
diff --git a/js/lost.js b/js/lost.js
index 58a7840..43a0b13 100644
--- a/js/lost.js
+++ b/js/lost.js
@@ -5,10 +5,56 @@
5// Helper functions 5// Helper functions
6// 6//
7 7
8//
9// Class: RandomLcg
10//
11function RandomLcg()
12{
13 this.iState = 0;
14 this.iSeed = 0;
15
16 this.inventSeed();
17}
18
19RandomLcg.prototype.setSeed = function setSeed( iSeed )
20{
21 this.iState = this.iSeed = iSeed;
22}
23
24RandomLcg.prototype.getSeed = function getSeed()
25{
26 return this.iSeed;
27}
28
29RandomLcg.prototype.inventSeed = function inventSeed()
30{
31 // Based on my reading it's safest to assume that we can get 16 bits worth
32 // of random data reliably. Let's build a 32 bit number from two 16 bit
33 // numbers.
34 this.setSeed(
35 (Math.floor(Math.random()*0xffff)) |
36 (Math.floor(Math.random()*0xffff)<<16)
37 );
38}
39
40RandomLcg.prototype.random = function random()
41{
42 this.iState = ((this.iState * 1103515245) + 12345) & 0x7fffffff;
43 return this.iState/(0x7fffffff+1);
44}
45
46RandomLcg.prototype.randInt = function randInt( max )
47{
48 return Math.floor(this.random() * max);
49}
50
51let lRand = new RandomLcg();
52
8// Just return a random integer between 0 and max, exclusive on the upper bound. 53// Just return a random integer between 0 and max, exclusive on the upper bound.
9function randInt( max ) 54function randInt( max )
10{ 55{
11 return Math.floor(Math.random() * max); 56 return lRand.randInt( max );
57// return Math.floor(Math.random() * max);
12} 58}
13 59
14// Return the id for the opposite direction of the direction given. 60// Return the id for the opposite direction of the direction given.
@@ -55,34 +101,47 @@ function Cell()
55// 101//
56function Position( iDims, ...Vals ) 102function Position( iDims, ...Vals )
57{ 103{
58 // Store dimension count 104 if( typeof iDims === 'string' )
59 this.iDims = iDims;
60
61 // Check to see if Vals is a non-empty array
62 if( Array.isArray(Vals) && Vals.length > 0 )
63 { 105 {
64 // Make sure Vals has the right number of elements 106 let sChunks = iDims.split(',');
65 if( Vals.length != iDims ) 107 this.iDims = sChunks.length;
108 this.aiValues = new Array(this.iDims);
109 for( let j = 0; j < this.iDims; j++ )
66 { 110 {
67 throw new Error( 111 this.aiValues[j] = parseInt( sChunks[j].trim(), 10 );
68 'Position must be initialized with no dimensional data, '+
69 'or the correct number of elements.');
70 } 112 }
71
72 // If it does have the correct number of elements, just
73 // use it instead of creating a new array
74 this.aiValues = Vals;
75 } 113 }
76 else 114 else
77 { 115 {
78 // We don't have values from the constructor, let's just 116 // Store dimension count
79 // create a new blank one... 117 this.iDims = iDims;
80 this.aiValues = new Array( this.iDims );
81 118
82 // ...and set the position to zero in all dimensions. 119 // Check to see if Vals is a non-empty array
83 for( let j = 0; j < this.iDims; j++ ) 120 if( Array.isArray(Vals) && Vals.length > 0 )
121 {
122 // Make sure Vals has the right number of elements
123 if( Vals.length != iDims )
124 {
125 throw new Error(
126 'Position must be initialized with no dimensional data, '+
127 'or the correct number of elements.');
128 }
129
130 // If it does have the correct number of elements, just
131 // use it instead of creating a new array
132 this.aiValues = Vals;
133 }
134 else
84 { 135 {
85 this.aiValues[j] = 0; 136 // We don't have values from the constructor, let's just
137 // create a new blank one...
138 this.aiValues = new Array( this.iDims );
139
140 // ...and set the position to zero in all dimensions.
141 for( let j = 0; j < this.iDims; j++ )
142 {
143 this.aiValues[j] = 0;
144 }
86 } 145 }
87 } 146 }
88} 147}
@@ -135,6 +194,17 @@ Position.prototype.equals = function equals( rhs )
135 return true; 194 return true;
136} 195}
137 196
197Position.prototype.toString = function toString()
198{
199 let ret = this.aiValues[0].toString();
200 for( let j = 1; j < this.aiValues.length; j++ )
201 {
202 ret += ',' + this.aiValues[j].toString();
203 }
204
205 return ret;
206}
207
138// 208//
139// Class: Map 209// Class: Map
140// 210//
@@ -144,7 +214,10 @@ function Map( Dimensions )
144 this.Dimensions = Dimensions; 214 this.Dimensions = Dimensions;
145 this.aWorms = new Array(); 215 this.aWorms = new Array();
146 this.pPlayer = null; 216 this.pPlayer = null;
217 this.pGoal = null;
147 this.ePlayerMoved = new Signal(); 218 this.ePlayerMoved = new Signal();
219 this.eVictory = new Signal();
220 this.ePlayerSetup = new Signal();
148 221
149 // Compute the total number of cells 222 // Compute the total number of cells
150 let iTotalSize = 1; 223 let iTotalSize = 1;
@@ -199,6 +272,11 @@ Map.prototype.movePlayer = function movePlayer( iDim, iDelta )
199 272
200 this.pPlayer.add( iDim, iDelta ); 273 this.pPlayer.add( iDim, iDelta );
201 this.ePlayerMoved.call(); 274 this.ePlayerMoved.call();
275
276 if( this.pPlayer.equals( this.pGoal ) )
277 {
278 this.eVictory.call();
279 }
202} 280}
203 281
204Map.prototype.isInside = function isInside( Position ) 282Map.prototype.isInside = function isInside( Position )
@@ -245,9 +323,14 @@ Map.prototype.get = function get( Position )
245 323
246Map.prototype.addWorm = function addWorm( pStart ) 324Map.prototype.addWorm = function addWorm( pStart )
247{ 325{
248 if( this.aWorms.length === 0 ) 326 if( this.pPlayer === null )
249 { 327 {
250 this.pPlayer = pStart; 328 this.pPlayer = pStart;
329 this.ePlayerSetup.call();
330 }
331 else if( this.pGoal === null )
332 {
333 this.pGoal = pStart;
251 } 334 }
252 let iNewId = this.aWorms.length+1; 335 let iNewId = this.aWorms.length+1;
253 this.aWorms.push( 336 this.aWorms.push(
@@ -513,9 +596,16 @@ function RenderCanvas2D( rMap, eParent )
513 this.rMap.ePlayerMoved.connect( 596 this.rMap.ePlayerMoved.connect(
514 RenderCanvas2D.prototype.render.bind( this ) 597 RenderCanvas2D.prototype.render.bind( this )
515 ); 598 );
599 this.rMap.ePlayerMoved.connect(
600 RenderCanvas2D.prototype.updateButtons.bind( this )
601 );
602 this.rMap.eVictory.connect(
603 RenderCanvas2D.prototype.setVictory.bind( this )
604 );
516 605
517 this.eCanvas = null; 606 this.eCanvas = null;
518 this.ctx = null; 607 this.ctx = null;
608 this.isSolved = false;
519 609
520 this.pExtPosition = new Position( rMap.getDims() ); 610 this.pExtPosition = new Position( rMap.getDims() );
521 611
@@ -535,13 +625,17 @@ function RenderCanvas2D( rMap, eParent )
535 eParent.appendChild( this.eCanvas ); 625 eParent.appendChild( this.eCanvas );
536 this.ctx = this.eCanvas.getContext("2d"); 626 this.ctx = this.eCanvas.getContext("2d");
537 this.ctx.lineWidth = 1.0; 627 this.ctx.lineWidth = 1.0;
628 this.ctx.font = Math.ceil(this.iIconSize) + 'px sans serif';
629 this.ctx.textBaseline = 'top';
630
631 this.aMoveButtons = [];
538 632
539 this.render(); 633 this.render();
540 634
541 let btnBox = document.createElement('div'); 635 this.btnBox = document.createElement('div');
542 eParent.appendChild( document.createElement('br') ); 636 eParent.appendChild( document.createElement('br') );
543 eParent.appendChild( document.createElement('br') ); 637 eParent.appendChild( document.createElement('br') );
544 eParent.appendChild( btnBox ); 638 eParent.appendChild( this.btnBox );
545 639
546 let cardTbl; 640 let cardTbl;
547 let cardRow; 641 let cardRow;
@@ -552,24 +646,32 @@ function RenderCanvas2D( rMap, eParent )
552 cardTbl.appendChild( cardRow ); 646 cardTbl.appendChild( cardRow );
553 cardRow.appendChild( document.createElement('td') ); 647 cardRow.appendChild( document.createElement('td') );
554 cardTd = document.createElement('td'); 648 cardTd = document.createElement('td');
555 cardTd.appendChild( createMoveButton( this.rMap, 1, -1, "North" ) ); 649 this.aMoveButtons[2] = cardTd.appendChild(
650 createMoveButton( this.rMap, 1, -1, "North" )
651 );
556 cardRow.appendChild( cardTd ); 652 cardRow.appendChild( cardTd );
557 cardRow.appendChild( document.createElement('td') ); 653 cardRow.appendChild( document.createElement('td') );
558 if( this.rMap.getDims() >= 3 ) 654 if( this.rMap.getDims() >= 3 )
559 { 655 {
560 cardTd = document.createElement('td'); 656 cardTd = document.createElement('td');
561 cardTd.appendChild( createMoveButton( this.rMap, 2, -1, "Up" ) ); 657 this.aMoveButtons[4] = cardTd.appendChild(
658 createMoveButton( this.rMap, 2, -1, "Up (^)" )
659 );
562 cardRow.appendChild( cardTd ); 660 cardRow.appendChild( cardTd );
563 } 661 }
564 662
565 cardRow = document.createElement('tr'); 663 cardRow = document.createElement('tr');
566 cardTbl.appendChild( cardRow ); 664 cardTbl.appendChild( cardRow );
567 cardTd = document.createElement('td'); 665 cardTd = document.createElement('td');
568 cardTd.appendChild( createMoveButton( this.rMap, 0, -1, "West" ) ); 666 this.aMoveButtons[0] = cardTd.appendChild(
667 createMoveButton( this.rMap, 0, -1, "West" )
668 );
569 cardRow.appendChild( cardTd ); 669 cardRow.appendChild( cardTd );
570 cardRow.appendChild( document.createElement('td') ); 670 cardRow.appendChild( document.createElement('td') );
571 cardTd = document.createElement('td'); 671 cardTd = document.createElement('td');
572 cardTd.appendChild( createMoveButton( this.rMap, 0, 1, "East" ) ); 672 this.aMoveButtons[1] = cardTd.appendChild(
673 createMoveButton( this.rMap, 0, 1, "East" )
674 );
573 cardRow.appendChild( cardTd ); 675 cardRow.appendChild( cardTd );
574 if( this.rMap.getDims() >= 3 ) 676 if( this.rMap.getDims() >= 3 )
575 { 677 {
@@ -580,23 +682,25 @@ function RenderCanvas2D( rMap, eParent )
580 cardTbl.appendChild( cardRow ); 682 cardTbl.appendChild( cardRow );
581 cardRow.appendChild( document.createElement('td') ); 683 cardRow.appendChild( document.createElement('td') );
582 cardTd = document.createElement('td'); 684 cardTd = document.createElement('td');
583 cardTd.appendChild( createMoveButton( this.rMap, 1, 1, "South" ) ); 685 this.aMoveButtons[3] = cardTd.appendChild(
686 createMoveButton( this.rMap, 1, 1, "South" )
687 );
584 cardRow.appendChild( cardTd ); 688 cardRow.appendChild( cardTd );
585 cardRow.appendChild( document.createElement('td') ); 689 cardRow.appendChild( document.createElement('td') );
586 if( this.rMap.getDims() >= 3 ) 690 if( this.rMap.getDims() >= 3 )
587 { 691 {
588 cardTd = document.createElement('td'); 692 cardTd = document.createElement('td');
589 cardTd.appendChild( createMoveButton( this.rMap, 2, 1, "Down" ) ); 693 this.aMoveButtons[5] = cardTd.appendChild(
694 createMoveButton( this.rMap, 2, 1, "Down (v)" )
695 );
590 cardRow.appendChild( cardTd ); 696 cardRow.appendChild( cardTd );
591 } 697 }
592 698
593 btnBox.appendChild( cardTbl ); 699 this.btnBox.appendChild( cardTbl );
594 700
595 let names = []; 701 let names = [];
596 names[6] = 'In'; 702// names[6] = 'In (<)';
597 names[7] = 'Out'; 703// names[7] = 'Out (>)';
598 names[8] = 'Through';
599 names[9] = 'Around';
600 704
601 cardTbl = document.createElement('table'); 705 cardTbl = document.createElement('table');
602 for( let j = 3; j < this.rMap.getDims(); j++ ) 706 for( let j = 3; j < this.rMap.getDims(); j++ )
@@ -607,24 +711,30 @@ function RenderCanvas2D( rMap, eParent )
607 let label; 711 let label;
608 712
609 if( typeof names[j*2] === 'undefined' ) 713 if( typeof names[j*2] === 'undefined' )
610 label = 'Dim ' + (j+1) + ': -'; 714 label = (j+1) + '-';
611 else 715 else
612 label = names[j*2]; 716 label = names[j*2];
613 717
614 cardTd = document.createElement('td'); 718 cardTd = document.createElement('td');
615 cardTd.appendChild( createMoveButton( this.rMap, j, -1, label ) ); 719 this.aMoveButtons[j*2] = cardTd.appendChild(
720 createMoveButton( this.rMap, j, -1, label )
721 );
616 cardRow.appendChild( cardTd ); 722 cardRow.appendChild( cardTd );
617 723
618 if( typeof names[j*2+1] === 'undefined' ) 724 if( typeof names[j*2+1] === 'undefined' )
619 label = 'Dim ' + (j+1) + ': +'; 725 label = (j+1) + '+';
620 else 726 else
621 label = names[j*2+1]; 727 label = names[j*2+1];
622 728
623 cardTd = document.createElement('td'); 729 cardTd = document.createElement('td');
624 cardTd.appendChild( createMoveButton( this.rMap, j, 1, label ) ); 730 this.aMoveButtons[j*2+1] = cardTd.appendChild(
731 createMoveButton( this.rMap, j, 1, label )
732 );
625 cardRow.appendChild( cardTd ); 733 cardRow.appendChild( cardTd );
626 } 734 }
627 btnBox.appendChild( cardTbl ); 735 this.btnBox.appendChild( cardTbl );
736
737 this.updateButtons();
628} 738}
629 739
630RenderCanvas2D.prototype = Object.create(Render.prototype); 740RenderCanvas2D.prototype = Object.create(Render.prototype);
@@ -649,6 +759,15 @@ RenderCanvas2D.prototype.render = function render()
649 p.set( 0, x ); 759 p.set( 0, x );
650 p.set( 1, y ); 760 p.set( 1, y );
651 let c = this.rMap.get( p ); 761 let c = this.rMap.get( p );
762
763 if( p.equals( this.rMap.pGoal ) )
764 {
765 let oldStyle = this.ctx.fillStyle;
766 this.ctx.fillStyle = 'palegreen';
767 this.ctx.fillRect( x*iSize+2, y*iSize+2, iSize-4, iSize-4 );
768 this.ctx.fillStyle = oldStyle;
769 }
770
652 if( (c.iWalls&1) === 0 && x === 0) 771 if( (c.iWalls&1) === 0 && x === 0)
653 { 772 {
654 this.ctx.moveTo( x*iSize, y*iSize ); 773 this.ctx.moveTo( x*iSize, y*iSize );
@@ -704,7 +823,7 @@ RenderCanvas2D.prototype.render = function render()
704 0, 823 0,
705 Math.PI*2.0, 824 Math.PI*2.0,
706 false 825 false
707 ); 826 );
708 this.ctx.fill(); 827 this.ctx.fill();
709 } 828 }
710} 829}
@@ -753,7 +872,7 @@ RenderCanvas2D.prototype.renderDirIcon = function renderDirIcon(
753 by 872 by
754 ); 873 );
755 break; 874 break;
756 875/*
757 case 6: 876 case 6:
758 // In 877 // In
759 this.ctx.moveTo( 878 this.ctx.moveTo(
@@ -785,9 +904,42 @@ RenderCanvas2D.prototype.renderDirIcon = function renderDirIcon(
785 by+this.iIconSize 904 by+this.iIconSize
786 ); 905 );
787 break; 906 break;
907*/
908 default:
909 let label =
910 Math.floor((iDir/2.0)+1).toString() +
911 (((iDir%2)===0)?'-':'+');
912 this.ctx.fillText(
913 label,
914 bx, by
915 );
916 break;
917 }
918}
919
920RenderCanvas2D.prototype.updateButtons = function updateButtons()
921{
922 let c = this.rMap.get( this.rMap.pPlayer );
923 for( let j = 0; j < this.rMap.getDims()*2; j++ )
924 {
925 this.aMoveButtons[j].disabled = (c.iWalls&(1<<j)) === 0;
788 } 926 }
789} 927}
790 928
929RenderCanvas2D.prototype.setVictory = function setVictory()
930{
931 while( this.btnBox.hasChildNodes() )
932 {
933 this.btnBox.removeChild( this.btnBox.firstChild );
934 }
935
936 let h1 = document.createElement('h1');
937 h1.appendChild(
938 document.createTextNode('You win!')
939 );
940 this.btnBox.appendChild( h1 );
941}
942
791RenderCanvas2D.prototype.setFloor = function setFloor( aFloor ) 943RenderCanvas2D.prototype.setFloor = function setFloor( aFloor )
792{ 944{
793 for( let j = 0; j < aFloor.length; j++ ) 945 for( let j = 0; j < aFloor.length; j++ )
@@ -800,7 +952,30 @@ RenderCanvas2D.prototype.setFloor = function setFloor( aFloor )
800// 952//
801// Initialize 953// Initialize
802// 954//
955
803let p = new Position( 4, 3, 3, 3, 3 ); 956let p = new Position( 4, 3, 3, 3, 3 );
957
958if( document.location.search !== '' )
959{
960 let dat = document.location.search.slice(1);
961 let datChunks = dat.split('&');
962 for( let j = 0; j < datChunks.length; j++ )
963 {
964 let pair = datChunks[j].split('=');
965 if( pair.length == 2 )
966 {
967 if( pair[0] == 'seed' )
968 {
969 lRand.setSeed( parseInt( decodeURIComponent( pair[1] ), 10 ) );
970 }
971 else if( pair[0] == 'dims' )
972 {
973 p = new Position( decodeURIComponent( pair[1] ) );
974 }
975 }
976 }
977}
978
804let m = new Map( p ); 979let m = new Map( p );
805let exit1 = new Position( p.getDims() ); 980let exit1 = new Position( p.getDims() );
806let exit2 = new Position( p.getDims() ); 981let exit2 = new Position( p.getDims() );
@@ -810,3 +985,40 @@ m.addWorm( exit2 );
810m.buildMaze(); 985m.buildMaze();
811 986
812let rend = new RenderCanvas2D( m, document.body ); 987let rend = new RenderCanvas2D( m, document.body );
988
989let formDiv = document.createElement('div');
990let form = document.createElement('form');
991let dims = document.createElement('input');
992dims.name = 'dims';
993dims.value = p.toString();
994let sub = document.createElement('input');
995sub.type = 'submit';
996sub.value = 'New maze!';
997form.appendChild( dims );
998form.appendChild( document.createTextNode(' ') );
999form.appendChild( sub );
1000let pExp = document.createElement('p');
1001pExp.appendChild(
1002 document.createTextNode(
1003 'Provide a number below for each dimension in the new maze. The numbers are the size of each dimension. Entering "10, 3" will create a maze that is 10 wide and 3 tall. Entering "5, 5, 5, 5, 5" will create a 5 dimensional maze, every dimension will be 5 wide.'
1004 )
1005 );
1006formDiv.appendChild( pExp );
1007formDiv.appendChild( form );
1008document.body.appendChild( document.createElement('br') );
1009document.body.appendChild( formDiv );
1010
1011let d = document.createElement('div');
1012let dLink = document.createElement('a');
1013let trgUrl = document.location.toString();
1014let query = trgUrl.indexOf('?');
1015if( query >= 0 )
1016{
1017 trgUrl = trgUrl.substring(0, query);
1018}
1019trgUrl += '?seed=' + lRand.getSeed() + '&dims=' + p.toString();
1020dLink.href = trgUrl;
1021dLink.appendChild( document.createTextNode('Share this maze!') );
1022d.appendChild(dLink);
1023document.body.appendChild( document.createElement('br') );
1024document.body.appendChild( d );