-
-
Save gullyn/95b2ab9e465317f1d4e4607cf6e94205 to your computer and use it in GitHub Desktop.
<body onload=z=c.getContext`2d`,setInterval(`c.width=W=150,Y<W&&P<Y&Y<P+E|9<p?z.fillText(S++${Y=`,9,9|z.fillRect(p`}*0,Y-=--M${Y+Y},P+E,9,W),P))):p=M=Y=S=6,p=p-6||(P=S%E,W)`,E=49) onclick=M=9><canvas id=c> |
Can someone please post an unminified version too? Would love to read and understand the brilliant algorithm.
@pratyushmittal Happy to explain!
The SVG solution actually fails to run if you put line breaks in it since most of it is a string, but I'll add them anyways for explanation purposes:
<svg id=c //Give the SVG an ID for reference later
onload="Y=G=T=0; //Must initialize Y (player Y location), G (gap distance from top), and T (time) to 0, each assignment returns the new result (0) which is used for the next assignment
setInterval(' //setInterval will repeatedly call the following function, acting as the game loop
//The following is particularly ordered to avoid needing parens https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
T%99 //Zero is treated a "falsey", so this will be "false" whenever T is divisible by 99 (happens at the moment the walls and player are both at Y=0)
? 0 //Ternary operator X ? Y : Z will return Y when X is truthy or Z when X is falsey, I'm using it as a concise if statement and don't care about the true case, so just return zero
: (Y>G) & (Y<(G+75)) //If player is below the top of the gap and above the bottom of the gap (single & is bitwise and, which works fine here)
? G=T%75 //If the player is in the gap, set a new gap height (not chosen randomly, instead is equal to the remainder of the current time/score divided by 75)
: V=Y=T=0; //If the player hit a wall, reset V (player velocity), Y (player height), and T (time/score) to zeros to restart
Y-=V-=.04; //Set V (player velocity) to V-.04 (velocity changed by gravity), returning the new V, THEN set Y to Y-newV to make the player move by the current velocity
c.innerHTML=` //Set the children of element with ID c (the svg) to the following path and text elements:
<path d=\' //Start path element, d (draw?) string quote must be escaped as we have already nested unescaped ", ', and `
//Path commands used below documented here: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#Path_commands
M ${99-T%99} ${G} //Move to location X=(99-T%99) Y=G, X will count down from 99 to 0 over and over as time increments, G is the distance from top of screen to the gap in the wall
h 9 //Move right 9 pixels relative to current location
V 0 //Move vertically to Y=0
h -9 //Move left 9 pixels relative to current location
Z //Return to starting location (completed top wall rectangle, automatically fills the completed shape with black)
v 75 //Move 75 pixels down (height of gap) from current location, now at top left corner of bottom wall
h 9 //Move right 9 pixels relative to current location
V 150 //Move vertically to Y=150 (bottom of image)
h -9 //Move left 9 pixels
Z //Move to starting location (overshoots to top left of top wall, filling in the now-enclosed bottom wall)
M 0 ${Y} //Move to location X=0 Y=Y (Y variable is the player's Y location)
v 9 //Move down 9 pixels
h 9 //Move right 9 pixels
v -9 //Move up 9 pixels
H 0 //Move horizontally to X=0, enclosing the player's 9x9 square (9 chosen for player and wall width due to being largest single-digit value)
\'/> //End of path string/element
<text y=11> //Start of text, y must be provided or text is off-screen, setting y=9 causes text to be partially off-screen but saves a byte
`+T++', //Increment time and concat as body for text, end function string as first argument of setInterval
9)" //Remainder of onload is the second argument to setInterval, setting an interval of 9ms (111 fps, chosen due to it being slowest one-digit rate)
onclick=V=2> //When you click on the SVG it sets the current velocity to 2 pixels/frame upwards
Maybe this explanation might help you find more improvements. 😉
@jmromrell Thanks a lot for the detailed explanation. It is very interesting.
A small riff on @jmromrell's excellent version allows this to scale better for 'full-screen' play but takes us back closer to 240 bytes.
<svg height="100%" width="100%" viewBox="0 0 290 190" id=c onload="Y=G=T=0;setInterval('T%99?0:Y>G&Y<G+75?G=T%75:V=Y=T=0;Y-=V-=.04;c.innerHTML=`<path d=\'M${99-T%99} ${G}h9V0h-9Zv75h9V150h-9ZM0 ${Y}v9h9v-9H0\'/><text y=11>${T++}`',9)"onclick=V=2>
It may be possible to address viewport scaling in fewer bytes with transform: scale(X)
(e.g #c { transform: scale(2);}
if we could avoid needing to set height/width/viewBox.
@kres0345 Mobile browsers do support Data URIs as shown on this Can I Use page.
Although another two bytes would be nice, I don't think that is enough of a reward for mobile compatibility.
You can actually just remove the "Z" and the "H0" from the path:
<svg id=c onload="Y=G=T=0;setInterval('T%99?0:Y>G&Y<G+75?G=T%75:V=Y=T=0;Y-=V-=.04;c.innerHTML=`<path d=\'M${99-T%99} ${G}h9V0h-9Zv75h9V150h-9M0 ${Y}v9h9v-9\'/><text y=11>`+T++',9)"onclick=V=2>
which is 192 bytes. This means the path is no longer closed, but it displays the same way because it is closed implicitly when it gets filled. The downside is that this is not very well defined behavior - I think - so some browsers could display it differently.
<svg id=c onload="Y=G=T=0;setInterval('T%99?Y-=V-=.1:Y>G&Y<G+60?G=T%75:V=Y=T=0;c.innerHTML=`<path d=\'M${99-T%99} ${G}h9V0h-9Zv60h9V150h-9M0 ${Y}v9h9v-9\'/><text y=11>`+T++',9)"onclick=V=3>
189 bytes. But a bit more difficult (like the real flappy bird)
it's been so long that I forgot how to run this...
You can try this URL:
data:text/html,%3Csvg id%3Dc onload%3D"Y%3DG%3DT%3D0%3BsetInterval('T%2599%3FY-%3DV-%3D.1%3AY%3EG%26Y%3CG%2B60%3FG%3DT%2575%3AV%3DY%3DT%3D0%3Bc.innerHTML%3D`%3Cpath d%3D%5C'M%24%7B99-T%2599%7D %24%7BG%7Dh9V0h-9Zv60h9V150h-9M0 %24%7BY%7Dv9h9v-9%5C'%2F%3E%3Ctext y%3D11%3E`%2BT%2B%2B'%2C9)"onclick%3DV%3D3%3E
thanks
Easier to read if you only replace % with %25 in the address bar:
data:text/html,<svg id=c onload="Y=G=T=0;setInterval('T%2599?Y-=V-=.1:Y>G&Y<G+60?G=T%2575:V=Y=T=0;c.innerHTML=`<path d=\'M${99-T%2599} ${G}h9V0h-9Zv60h9V150h-9M0 ${Y}v9h9v-9\'/><text y=11>`+T++',9)"onclick=V=3>
@jmromrell I'm guessing either the slashes or the backslashes. The website I looked at said & is not dataurl safe and we need to encode it, not sure how we got away with that