summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Buland <Mike.Buland@mjfirm.com>2016-10-21 15:06:14 -0600
committerMike Buland <Mike.Buland@mjfirm.com>2016-10-21 15:06:14 -0600
commit1545f1d23e2dd8956593c6294518c4f3dc5c2028 (patch)
treee7c548189196075671cc9ffe9591ec191629f31d
parente3a6b64a9dcae8e4d1d02969e28779be1337af7e (diff)
downloadlost-1545f1d23e2dd8956593c6294518c4f3dc5c2028.tar.gz
lost-1545f1d23e2dd8956593c6294518c4f3dc5c2028.tar.bz2
lost-1545f1d23e2dd8956593c6294518c4f3dc5c2028.tar.xz
lost-1545f1d23e2dd8956593c6294518c4f3dc5c2028.zip
A simple get form lets you pick maze dims.
It would be cooler to have something more dynamic and interactive, but this is pretty good for now. It should probably try to detect issues with your syntax, too, but that can come later. You can also share maps now. That's a fun feature overall.
-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 );