Click on the field to position your ball! Click BELOW the field to confirm your move and let the AI work. The winner will be hyped.
for(_='"+(u.~*g-10Q--;)Ofor(NzONM+i*L4>YKo%4J=-1H)+G>3F},E=0D20CS[B][y]ABlA[k]@BzA[x]c.fill*cos(I=1*a/(m+a=[],){NP*H.m.&&(=4;);d.lengthH;1>=o%16/4|0e(J,Y,)sin(++)0,||), if(Y=R(P,J,return n));*C*I-)zMBz]yyOBzA=[0];P,nDo6;o--,1 !!(n)function(s,k,l,m){Nae3,SI=Y=g=7o5,,b.onclick=n}s.pageY>400){N,h=o;o>D;!n,}n(o=h++ +(4*random()|0) n=K)break},Y=4,oH}else N;KB][Y][J]D o++, !(KEe=Nuu;uNvv;vNtt;ttvum=i=-3;3>=i;iz=lLu,y=kLv,x=sLt,0>zzF0>yyF0>xxF(!i==P)&&m++;mF)1}ER=Ny=3;0!=@;y--)0==y)4;m(@=s yEf=i=sGl*I m=s*-IGl u=iGCv=(k+C0) {x:u,y:v,z:m}EsetInterval(Nc.clearRect(a,a dzMyD;5>y;yNxxOp=yF?.1:,pm=f(xQy*g/2+n,zQ0 d.push({m:m,p:p,r:g/4-m.z/45-p-1,l:n&&p==P?6+4:6*abs(p)})Nd.sort(sz-kz} z=;Mu=d[z],xD;x<u.r;xc.beginPath( c.arc(ux,uy,u.r-x,6.3,1 Style="hsl(~p>0?C0:C0*I)G",90%,~l*x80G"%)",(I+=.01E30';g=/[^ -?IPR-}]/.exec(_);)with(_.split(g))_=join(shift());with(Math)eval(_)
Zm9yKF89JyIrKHUufipnLTEwUS0tOylPZm9yKE56T05NK2kqTDQ+WUtvJTRKPS0xSCkrRz4zRn0sRT0wRDIwQ1NbQl1beV1BQmxBW2tdQEJ6QVt4XR9jLmZpbGweKmNvcyhJHT0xHCphLyhtK2EbPVtdLBope04ZUCpIGC5tLhcmJigWPTQ7FSk7FGQubGVuZ3RoE0g7MT49Em8lMTYvNHwwEWUoSixZLBEpEHNpbigPKyspDjAsDHx8CyksCWlmKAhZPVIoUCxKLBEHcmV0dXJuIAYIEBZuHCkpBjsFKg9DKkktEykEehVNQnpdGnkVeU9CekE9WwwMDDBdO1AcLG5EA28cNjtvLS0WBywxCSEQCyEobhwpFBQCZnVuY3Rpb24ocyxrLGwsbSl7AU5hHGUzLFMaST1ZPQxnPTcMbxw1LAMsYi5vbmNsaWNrPQEIbhkDfQhzLnBhZ2VZPjQwMCl7BU4YLGg9bztvPkQ7GQIIIW4ZGCwCGH0Ibgsobz1oKysgKyg0KnJhbmRvbSgpfDApCQcJbj0MSylicmVha30FGCxZPTQsb0h9ZWxzZSBOO0sWQhFdW1ldW0pdRAlvKyssBwkhKEsUFEVlPQFOdRJ1O3UOTnYSdjt2Dk50EnQ7dA4IdAt2C3UZbT0MaT0tMzszPj1pO2kOej1sTHUseT1rTHYseD1zTHQsMD56C3pGCzA+eQt5RgswPngLeEYLKCFpCx89PVApJiZtKys7CG1GKQYxfUVSPQFOeT0zOzAhPUA7eS0tKQgwPT15KQY0OwZtCyhAPXMJeUVmPQEGaT1zHUdsKg9JCW09cyotD0lHbB0JdT1pG0dDDHY9KGsrQzApGwl7eDp1LHk6dix6Om19RXNldEludGVydmFsKAFOYy5jbGVhclJlY3QoDAxhLGEJZBp6FU15RDs1Pnk7eQ5OeBV4T3A9eUY/LjE6HyxwFm09Zih4UQx5KmcvMituBCx6UTAJZC5wdXNoKHttOm0scDpwLHI6Zy80LW0uei80NS1wBC0xLGw6biYmcD09UD82KzQEOjYqYWJzKHApfSkUTmQuc29ydCgBBnMXei1rF3p9CXo9EztNdT1kW3pdLHhEO3g8dS5yO3gOYy5iZWdpblBhdGgoCWMuYXJjKHUXeCx1F3ksdS5yLXgsDDYuMywxCR5TdHlsZT0iaHNsKH5wPjA/QzA6QzAqD0kpRyIsOTAlLH5sKngLODBHIiUpIiweKBRJKz0uMDFFMzAUJztnPS9bXiAtP0lQUi19XS8uZXhlYyhfKTspd2l0aChfLnNwbGl0KGcpKV89am9pbihzaGlmdCgpKTt3aXRoKE1hdGgpZXZhbChfKQ==
/*
* Hyped Connect Four 3D by Oliver Güther
* Written for JS1k 2015 contest
*
* Thanks to JS1k team/contributors/participants, RegPack and UglifyJS!
*
* Minified and compressed with UglifyJS 2 and RegPack 3.0.2
* Some hand opt. after Uglify like return; -> return ;
*
* Code is written very redundant at some parts to feed RegPack.
*
* used global vars: abc ACELPUWXY
* global vars used for local purpose: dipusvxyz
* used function name vars: BHJ with params: jklm
*/
// No Math shortcuts - RegPack will do it better
// M = Math, R = Math.random, S = Math.sin, O = Math.cos,
E = 1e3, // perspective and -cameraZ
C = [], // the game cube 4x4x4
A = // cube turning angle
Y = 0, // y for last position
L = 70, // grid size
X = 15; // x + 4*z for last clicked position;
// 15 for consistent user experience AND allow AI to do the first move
//P = 1 // player 1 or -1
//W = 0 // win marker
// init cube C
// RegPack: using a one dim array[64] instead 3 dim array doesn't seem to help
// RegPack: writing this init code twice will gain 6B
// vs putting a undefined check for C in render function p = y > 3 ? .1 : C[0] && C[z][y][x];
for (z = 4; z--;)
for (C[z] = [], y = 4; y--;)
C[z][y] = [0,0,0,0];
P = 1;
W = 0;
// user input handler, j = event
// written very redundant: RegPack is hungry
b.onclick = function(j, k, l, m) {
// win? re-init after mouse click
if (W) {
for (z = 4; z--;)
for (C[z] = [], y = 4; y--;)
C[z][y] = [0,0,0,0];
P = 1;
W = 0;
//X = 15; optional
}
// user move confirmed -> AI move; optional add j.buttons > 0 || j.which > 1 ||
if (j.pageY > 400) {
// user win?
if (H(X%4, Y, X%16/4|0) && (W = 1))
return;
P*=-1;
U = X; // save user move; RegPack: don't do U <-> X
for(;X >= 0;) {
// check for direct win
for (X = 16;X--;) {
Y = B(P, X%4, X%16/4|0, 1)
if (H(X%4, Y, X%16/4|0) && (W = 1))
break;
}
if (!W) {
// check for opponent (user) win; RegPack: wanted redundancy to the loop above
P*=-1; // RegPack: prefer this to param for H()
for (X = 16;X--;) {
Y = B(P, X%4, X%16/4|0, 1)
if (H(X%4, Y, X%16/4|0) && (W = 1))
break;
}
P*=-1;
}
if (!W) {
// try a move close to user pos (higher p. to block traps)
X = U++ + (4*Math.random()|0);
// by adding variety AI will play more interesting, but weaker in blocking traps
// ..(4*Math.random()|0 || 16*Math.random()|0);
}
Y = B(P, X%4, X%16/4|0); // now set the ball (non-virtual)
W = 0; // reset win cause it maybe marked for virtual move
if (Y < 4) break;
}
// AI win?
if (H(X%4, Y, X%16/4|0) && (W = 1))
return;
P*=-1;
Y = 4;
X = -1; // prevent back-to-back AI moves
} else { // positioning mode
for(;;) { // loop to skip illegal moves
if (Y < 4) // RegPack: don't replace with Y < 4 && ... here! original: if (X >= 0 && Y < 4)
C[X%16/4|0][Y][X%4] = 0; // clear last test pos
X++;
Y = B(P, X%4, X%16/4|0);
if (Y < 4) break;
}
}
}
// winCheck(x, y, z) for current player P
// thread counter, rating and Minimax dev suspended because of time and 1K limit
H = function(j, k, l, m) {
for (u = -1; u <= 1; u++) {
for (v = -1; v <= 1; v++) {
for (w = -1; w <= 1; w++) {
// dont need double checks for opp. dir
if (!w && !v && !u) // no dir; Uglify/RegPack will invert/optimze this
continue;
m = 0; // ball counter for P
for (i = -3; i <= 3; i++) {
z = l + i*u, y = k + i*v, x = j + i*w;
if (z < 0 || z > 3 || y < 0 || y > 3 || x < 0 || x > 3) {
continue;
}
(!i || C[z][y][x] == P) && m++; // !i -> trust that given pos is for P -> opt. for 'virtual' AI win checks
}
if (m > 3)
return 1;
}
}
}
}
// setBall(player, x, z, virtualMove); returns 4 for invalid move
B = function (j, k, l, m) {
for (y = 3;C[l][y][k] != 0;y--) {
if (y == 0) return 4;
}
if (!m) C[l][y][k] = j; // RegPack: no gain for !m && (C...)
return y;
};
// scene transform and project3d(x, y, z)
// Optional: put it inline
J = function (j, k, l, m) {
i = j * Math.cos(A) + l * Math.sin(A), // x'
m = j * -Math.sin(A) + l * Math.cos(A); // z'
u = (i * E) / (m + E) + 200,
v = ((k + 200) * E) / (m + E);
return {x: u, y: v, z: m};
};
// render loop
setInterval(function(j, k, l, m) {
c.clearRect(0,0,E,E);
d = []; // push ball render data in array for z-sort
for (z = 4; z--;) {
for (y = 0; y < 5; y++) { // RegPack: y = 5; --y; -> no gain; 5 for ground plate
for (x = 4; x--;) {
p = y > 3 ? .1 : C[z][y][x];
if (p) {
m = J(x*L - 100, y*L/2 + W*Math.sin(A*20 - d.length), z*L - 100); // RegPack: 1.5*L replaced with 100 for 4-6 bytes gain
d.push({
m: m,
p: p,
// ball wobbel effect
r: L/4 - m.z/45 - (p*Math.sin(A*20 - d.length)) - 1,
// put light with hype for winner in this loop to prevent flickering
l: W && p == P ? 6 + 4*Math.sin(A*20 - d.length) : 6 * Math.abs(p) });
}
}
}
}
d.sort(function(j, k, l, m) {return j.m.z - k.m.z;}); // z-sort
for (z = d.length; z--;) { // 1B gain for unsafe op: for (z in d)
u = d[z]; // u = currBall
// p = u.p; or with(u) // RegPack: no gain
// c.createRadialGradient() + addColorStop() // RegPack: no gain
for (x = 0; x < u.r; x++) { // Not: x = u.r; x--; x+=2 for better performance and little less quality
c.beginPath();
c.arc(u.m.x, u.m.y, u.r - x, 0, 6.3, 1); // RegPack: gain 1B to 2*Math.PI
// some light effects and color cycling
c.fillStyle = 'hsl(' + (u.p > 0 ? 200 : 200 * Math.sin(A))+ ',90%,' + (u.l * x || 80) + '%)';
c.fill();
}
}
A += .01; // turn cube
}, 30);