├── README.md ├── demo.html ├── js └── tetris.js ├── project.clj ├── src └── tetris │ ├── core.clj │ ├── core.cljs │ └── game.cljc ├── tetris-1.0-standalone.jar └── tetris.html /README.md: -------------------------------------------------------------------------------- 1 | Sample Tetris game in Clojure/Script 2 | 3 | [demo](https://rawgit.com/yogthos/Clojure-Tetris/master/demo.html) 4 | 5 | to compile Clojure version run 6 | ```bash 7 | lein uberjar 8 | ``` 9 | to compile ClojureScript version run 10 | ```bash 11 | lein cljsbuild once 12 | ``` 13 | 14 | Open `tetris.html` in a browser to play the game. 15 | 16 | Copyright (C) 2010 Yogthos 17 | 18 | Distributed under the Eclipse Public License License. 19 | -------------------------------------------------------------------------------- /demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Canvas Test 6 | 7 | 8 | 9 |
10 |
11 | 12 | This text is displayed if your browser does not support HTML5 Canvas. 13 | 14 |
15 | 16 |
17 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /js/tetris.js: -------------------------------------------------------------------------------- 1 | if(typeof Math.imul == "undefined" || (Math.imul(0xffffffff,5) == 0)) { 2 | Math.imul = function (a, b) { 3 | var ah = (a >>> 16) & 0xffff; 4 | var al = a & 0xffff; 5 | var bh = (b >>> 16) & 0xffff; 6 | var bl = b & 0xffff; 7 | // the shift by 0 fixes the sign on the high part 8 | // the final |0 converts the unsigned value into a signed value 9 | return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0)|0); 10 | } 11 | } 12 | 13 | 14 | ;(function(){ 15 | var f; 16 | function r(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"== 17 | b&&"undefined"==typeof a.call)return"object";return b}var aa="closure_uid_"+(1E9*Math.random()>>>0),ba=0;function ca(a,b){for(var c in a)b.call(void 0,a[c],c,a)};function da(a,b){this.U=[];this.Ua=b;for(var c=!0,d=a.length-1;0<=d;d--){var e=a[d]|0;c&&e==b||(this.U[d]=e,c=!1)}}var fa={};function ga(a){if(-128<=a&&128>a){var b=fa[a];if(b)return b}b=new da([a|0],0>a?-1:0);-128<=a&&128>a&&(fa[a]=b);return b}function ia(a){if(isNaN(a)||!isFinite(a))return ka;if(0>a)return ia(-a).ba();for(var b=[],c=1,d=0;a>=c;d++)b[d]=a/c|0,c*=la;return new da(b,0)}var la=4294967296,ka=ga(0),oa=ga(1),pa=ga(16777216);f=da.prototype; 18 | f.$b=function(){return 0a||36>>0).toString(a),c=e;if(c.Ha())return g+d;for(;6>g.length;)g="0"+g;d=""+g+d}};function qa(a,b){return 0>b?0:bthis.compare(pa)};f.Cb=function(a){return 0>=this.compare(a)};f.compare=function(a){a=this.ub(a);return a.ka()?-1:a.Ha()?0:1};f.ba=function(){return this.Yb().add(oa)}; 21 | f.add=function(a){for(var b=Math.max(this.U.length,a.U.length),c=[],d=0,e=0;e<=b;e++){var g=d+(qa(this,e)&65535)+(qa(a,e)&65535),h=(g>>>16)+(qa(this,e)>>>16)+(qa(a,e)>>>16),d=h>>>16,g=g&65535,h=h&65535;c[e]=h<<16|g}return new da(c,c[c.length-1]&-2147483648?-1:0)};f.ub=function(a){return this.add(a.ba())}; 22 | f.multiply=function(a){if(this.Ha()||a.Ha())return ka;if(this.ka())return a.ka()?this.ba().multiply(a.ba()):this.ba().multiply(a).ba();if(a.ka())return this.multiply(a.ba()).ba();if(this.Bb()&&a.Bb())return ia(this.hb()*a.hb());for(var b=this.U.length+a.U.length,c=[],d=0;d<2*b;d++)c[d]=0;for(d=0;d>>16,h=qa(this,d)&65535,k=qa(a,e)>>>16,l=qa(a,e)&65535;c[2*d+2*e]+=h*l;ta(c,2*d+2*e);c[2*d+2*e+1]+=g*l;ta(c,2*d+2*e+1);c[2*d+2*e+1]+=h*k; 23 | ta(c,2*d+2*e+1);c[2*d+2*e+2]+=g*k;ta(c,2*d+2*e+2)}for(d=0;d>>16,a[b]&=65535} 24 | function sa(a,b){if(b.Ha())throw Error("division by zero");if(a.Ha())return ka;if(a.ka())return b.ka()?sa(a.ba(),b.ba()):sa(a.ba(),b).ba();if(b.ka())return sa(a,b.ba()).ba();if(30=g?1:Math.pow(2,g-48);h=ia(e);for(var k=h.multiply(b);k.ka()||k.Vb(d);)e-=g,h=ia(e),k=h.multiply(b);h.Ha()&&(h=oa);c=c.add(h);d=d.ub(k)}return c}f.Yb=function(){for(var a=this.U.length,b=[],c=0;c>5;a%=32;for(var c=this.U.length+b+(0>>32-a:qa(this,e-b);return new da(d,this.Ua)}; 26 | f.ab=function(a){var b=a>>5;a%=32;for(var c=this.U.length-b,d=[],e=0;e>>a|qa(this,e+b+1)<<32-a:qa(this,e+b);return new da(d,this.Ua)};function ua(a,b){null!=a&&this.append.apply(this,arguments)}f=ua.prototype;f.Pa="";f.set=function(a){this.Pa=""+a};f.append=function(a,b,c){this.Pa+=String(a);if(null!=b)for(var d=1;d>>16&65535)*d+c*(b>>>16&65535)<<16>>>0)|0};function Lb(a){a=Kb(a|0,-862048943);return Kb(a<<15|a>>>-15,461845907)} 51 | function Mb(a,b){var c=(a|0)^(b|0);return Kb(c<<13|c>>>-13,5)+-430675100|0}function Nb(a,b){var c=(a|0)^b,c=Kb(c^c>>>16,-2048144789),c=Kb(c^c>>>13,-1028477387);return c^c>>>16}function Ob(a){a:{var b=1;for(var c=0;;)if(b>2)} 54 | function Ub(a,b,c,d,e){this.gb=a;this.name=b;this.Oa=c;this.Va=d;this.ha=e;this.h=2154168321;this.A=4096}f=Ub.prototype;f.toString=function(){return this.Oa};f.equiv=function(a){return this.o(null,a)};f.o=function(a,b){return b instanceof Ub?this.Oa===b.Oa:!1}; 55 | f.call=function(){function a(a,b,c){return I.g?I.g(b,this,c):I.call(null,b,this,c)}function b(a,b){return I.b?I.b(b,this):I.call(null,b,this)}var c=null,c=function(c,e,g){switch(arguments.length){case 2:return b.call(this,0,e);case 3:return a.call(this,0,e,g)}throw Error("Invalid arity: "+(arguments.length-1));};c.b=b;c.g=a;return c}();f.apply=function(a,b){return this.call.apply(this,[this].concat(Ha(b)))};f.a=function(a){return I.b?I.b(a,this):I.call(null,a,this)}; 56 | f.b=function(a,b){return I.g?I.g(a,this,b):I.call(null,a,this,b)};f.K=function(){return this.ha};f.N=function(a,b){return new Ub(this.gb,this.name,this.Oa,this.Va,b)};f.I=function(){var a=this.Va;return null!=a?a:this.Va=a=Tb(Ob(this.name),Rb(this.gb))};f.M=function(a,b){return G(b,this.Oa)}; 57 | function J(a){if(null==a)return null;if(null!=a&&(a.h&8388608||u===a.Pb))return a.G(null);if(Ca(a)||"string"===typeof a)return 0===a.length?null:new K(a,0,null);if(x(kb,a))return lb(a);throw Error([A.a(a),A.a(" is not ISeqable")].join(""));}function L(a){if(null==a)return null;if(null!=a&&(a.h&64||u===a.Qa))return a.$(null);a=J(a);return null==a?null:D(a)}function Wb(a){return null!=a?null!=a&&(a.h&64||u===a.Qa)?a.da(null):(a=J(a))?F(a):Xb:Xb} 58 | function N(a){return null==a?null:null!=a&&(a.h&128||u===a.lb)?a.ca(null):J(Wb(a))}var O=function O(b){for(var c=[],d=arguments.length,e=0;;)if(e=d)return-1;!(0c&&(c+=d,c=0>c?0:c);for(;;)if(cc?d+c:c;for(;;)if(0<=c){if(O.b(lc?lc(a,c):mc.call(null,a,c),b))return c;--c}else return-1}function nc(a,b){this.c=a;this.j=b}nc.prototype.aa=function(){return this.ja?0:a};f.I=function(){return ac(this)};f.o=function(a,b){return oc.b?oc.b(this,b):oc.call(null,this,b)};f.W=function(a,b){return ic(this.c,b,this.c[this.j],this.j+1)};f.X=function(a,b,c){return ic(this.c,b,c,this.j)};f.$=function(){return this.c[this.j]}; 67 | f.da=function(){return this.j+1>1&1431655765;a=(a&858993459)+(a>>2&858993459);return 16843009*(a+(a>>4)&252645135)>>24} 100 | var A=function A(b){for(var c=[],d=arguments.length,e=0;;)if(ea?0:a-1>>>5<<5}function Kd(a,b,c){for(;;){if(0===b)return c;var d=Hd(a);d.c[0]=c;c=d;b-=5}}var Ld=function Ld(b,c,d,e){var g=new Gd(d.C,Ha(d.c)),h=b.i-1>>>c&31;5===c?g.c[h]=e:(d=d.c[h],null!=d?(c-=5,b=Ld.B?Ld.B(b,c,d,e):Ld.call(null,b,c,d,e)):b=Kd(null,c-5,e),g.c[h]=b);return g}; 155 | function Md(a,b){throw Error([A.a("No item "),A.a(a),A.a(" in vector of length "),A.a(b)].join(""));}function Nd(a,b){if(b>=Id(a))return a.Y;for(var c=a.root,d=a.shift;;)if(0>>d&31],d=e;else return c.c}function Od(a,b){return 0<=b&&b>>c&31;c-=5;d=d.c[k];b=Pd.D?Pd.D(b,c,d,e,g):Pd.call(null,b,c,d,e,g);h.c[k]=b}return h}; 156 | function Qd(a,b,c,d,e,g){this.j=a;this.nb=b;this.c=c;this.oa=d;this.start=e;this.end=g}Qd.prototype.aa=function(){return this.j=this.i)return new K(this.Y,0,null);a:{var a=this.root;for(var b=this.shift;;)if(0this.i-Id(this)){for(var c=this.Y.length,d=Array(c+1),e=0;;)if(e>>5>1<b)a=new X(null,b,5,Y,a,null);else for(var c=32,d=(new X(null,32,5,Y,a.slice(0,32),null)).Wa(null);;)if(cb||this.end<=this.start+b?Md(b,this.end-this.start):B.b(this.oa,this.start+b)}; 175 | f.Z=function(a,b,c){return 0>b||this.end<=this.start+b?c:B.g(this.oa,this.start+b,c)};f.cb=function(a,b,c){a=this.start+b;if(0>b||this.end+1<=a)throw Error([A.a("Index "),A.a(b),A.a(" out of bounds [0,"),A.a(this.L(null)),A.a("]")].join(""));b=this.l;c=vc.g(this.oa,a,c);var d=this.start,e=this.end;a+=1;a=e>a?e:a;return ce.D?ce.D(b,c,d,a,null):ce.call(null,b,c,d,a,null)};f.ja=function(){return Rd(this.oa,this.start,this.end)};f.K=function(){return this.l};f.L=function(){return this.end-this.start}; 176 | f.I=function(){var a=this.m;return null!=a?a:this.m=a=ac(this)};f.o=function(a,b){return oc(this,b)};f.W=function(a,b){return ec(this,b)};f.X=function(a,b,c){return fc(this,b,c)};f.ra=function(a,b,c){if("number"===typeof b)return this.cb(null,b,c);throw Error("Subvec's key for assoc must be a number.");};f.G=function(){var a=this;return function(b){return function d(e){return e===a.end?null:T(B.b(a.oa,e),new Yc(null,function(){return function(){return d(e+1)}}(b),null,null))}}(this)(a.start)}; 177 | f.N=function(a,b){return ce.D?ce.D(b,this.oa,this.start,this.end,this.m):ce.call(null,b,this.oa,this.start,this.end,this.m)};f.S=function(a,b){var c=this.l,d=ab(this.oa,this.end,b),e=this.start,g=this.end+1;return ce.D?ce.D(c,d,e,g,null):ce.call(null,c,d,e,g,null)}; 178 | f.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.H(null,c);case 3:return this.Z(null,c,d)}throw Error("Invalid arity: "+(arguments.length-1));};a.b=function(a,c){return this.H(null,c)};a.g=function(a,c,d){return this.Z(null,c,d)};return a}();f.apply=function(a,b){return this.call.apply(this,[this].concat(Ha(b)))};f.a=function(a){return this.H(null,a)};f.b=function(a,b){return this.Z(null,a,b)};be.prototype[Fa]=function(){return Zb(this)}; 179 | function ce(a,b,c,d,e){for(;;)if(b instanceof be)c=b.start+c,d=b.start+d,b=b.oa;else{var g=R(b);if(0>c||0>d||c>g||d>g)throw Error("Index out of bounds");return new be(a,b,c,d,e)}}function $d(a){for(var b=[],c=arguments.length,d=0;;)if(d>>c&31;if(5===c)b=e;else{var h=d.c[g];null!=h?(c-=5,b=ee.B?ee.B(b,c,h,e):ee.call(null,b,c,h,e)):b=Kd(b.root.C,c-5,e)}d.c[g]=b;return d};function Sd(a,b,c,d){this.i=a;this.shift=b;this.root=c;this.Y=d;this.A=88;this.h=275}f=Sd.prototype; 182 | f.Ya=function(a,b){if(this.root.C){if(32>this.i-Id(this))this.Y[this.i&31]=b;else{var c=new Gd(this.root.C,this.Y),d=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];d[0]=b;this.Y=d;if(this.i>>>5>1<>>d&31,n=g(d-5,h.c[m]);h.c[m]=n}return h}}(a).call(null,a.shift,a.root);a.root=d}return a}if(b===a.i)return a.Ya(null,c);throw Error([A.a("Index "),A.a(b),A.a(" out of bounds for TransientVector of length"),A.a(a.i)].join(""));}throw Error("assoc! after persistent!");} 186 | f.L=function(){if(this.root.C)return this.i;throw Error("count after persistent!");};f.H=function(a,b){if(this.root.C)return Od(this,b)[b&31];throw Error("nth after persistent!");};f.Z=function(a,b,c){return 0<=b&&bb?4:2*(b+1));Gc(this.c,0,c,0,2*b);return new Ae(a,this.R,c)};f.eb=function(){return Be?Be(this.c):Ce.call(null,this.c)};f.Ma=function(a,b,c,d){var e=1<<(b>>>a&31);if(0===(this.R&e))return d;var g=Sc(this.R&e-1),e=this.c[2*g],g=this.c[2*g+1];return null==e?g.Ma(a+5,b,c,d):we(c,e)?g:d}; 208 | f.ma=function(a,b,c,d,e,g){var h=1<<(c>>>b&31);var k=Sc(this.R&h-1);if(0===(this.R&h)){var l=Sc(this.R);if(2*l>>b&31]=Ee.ma(a,b+5,c,d,e,g);for(e=d=0;;)if(32>d)0!== 209 | (this.R>>>d&1)&&(k[d]=null!=this.c[e]?Ee.ma(a,b+5,Sb(this.c[e]),this.c[e],this.c[e+1],g):this.c[e+1],e+=2),d+=1;else break;return new Fe(a,l+1,k)}b=Array(2*(l+4));Gc(this.c,0,b,0,2*k);b[2*k]=d;b[2*k+1]=e;Gc(this.c,2*k,b,2*(k+1),2*(l-k));g.qa=!0;a=this.Ra(a);a.c=b;a.R|=h;return a}l=this.c[2*k];h=this.c[2*k+1];if(null==l)return l=h.ma(a,b+5,c,d,e,g),l===h?this:ye(this,a,2*k+1,l);if(we(d,l))return e===h?this:ye(this,a,2*k+1,e);g.qa=!0;g=b+5;d=Ge?Ge(a,g,l,h,c,d,e):He.call(null,a,g,l,h,c,d,e);e=2*k;k= 210 | 2*k+1;a=this.Ra(a);a.c[e]=null;a.c[k]=d;return a}; 211 | f.la=function(a,b,c,d,e){var g=1<<(b>>>a&31);var h=Sc(this.R&g-1);if(0===(this.R&g)){var k=Sc(this.R);if(16<=k){h=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];h[b>>>a&31]=Ee.la(a+5,b,c,d,e);for(d=c=0;;)if(32>c)0!==(this.R>>>c&1)&&(h[c]=null!=this.c[d]?Ee.la(a+5,Sb(this.c[d]),this.c[d],this.c[d+1],e):this.c[d+1],d+=2),c+=1;else break;return new Fe(null,k+1,h)}a=Array(2*(k+1));Gc(this.c, 212 | 0,a,0,2*h);a[2*h]=c;a[2*h+1]=d;Gc(this.c,2*h,a,2*(h+1),2*(k-h));e.qa=!0;return new Ae(null,this.R|g,a)}var l=this.c[2*h];g=this.c[2*h+1];if(null==l)return k=g.la(a+5,b,c,d,e),k===g?this:new Ae(null,this.R,xe(this.c,2*h+1,k));if(we(c,l))return d===g?this:new Ae(null,this.R,xe(this.c,2*h+1,d));e.qa=!0;e=this.R;k=this.c;a+=5;a=Ie?Ie(a,l,g,b,c,d):He.call(null,a,l,g,b,c,d);c=2*h;h=2*h+1;d=Ha(k);d[c]=null;d[h]=a;return new Ae(null,e,d)};f.ja=function(){return new ze(this.c,0,null,null)}; 213 | var Ee=new Ae(null,0,[]);function Je(a,b,c){this.c=a;this.j=b;this.na=c}Je.prototype.aa=function(){for(var a=this.c.length;;){if(null!=this.na&&this.na.aa())return!0;if(this.j>>a&31];return null!=e?e.Ma(a+5,b,c,d):d};f.ma=function(a,b,c,d,e,g){var h=c>>>b&31,k=this.c[h];if(null==k)return a=ye(this,a,h,Ee.ma(a,b+5,c,d,e,g)),a.i+=1,a;b=k.ma(a,b+5,c,d,e,g);return b===k?this:ye(this,a,h,b)}; 215 | f.la=function(a,b,c,d,e){var g=b>>>a&31,h=this.c[g];if(null==h)return new Fe(null,this.i+1,xe(this.c,g,Ee.la(a+5,b,c,d,e)));a=h.la(a+5,b,c,d,e);return a===h?this:new Fe(null,this.i,xe(this.c,g,a))};f.ja=function(){return new Je(this.c,0,null)};function Me(a,b,c){b*=2;for(var d=0;;)if(da?d:we(c,this.c[a])?this.c[a+1]:d}; 217 | f.ma=function(a,b,c,d,e,g){if(c===this.Ka){b=Me(this.c,this.i,d);if(-1===b){if(this.c.length>2*this.i)return b=2*this.i,c=2*this.i+1,a=this.Ra(a),a.c[b]=d,a.c[c]=e,g.qa=!0,a.i+=1,a;c=this.c.length;b=Array(c+2);Gc(this.c,0,b,0,c);b[c]=d;b[c+1]=e;g.qa=!0;d=this.i+1;a===this.C?(this.c=b,this.i=d,a=this):a=new Ne(this.C,this.Ka,d,b);return a}return this.c[b+1]===e?this:ye(this,a,b+1,e)}return(new Ae(a,1<<(this.Ka>>>b&31),[null,this,null,null])).ma(a,b,c,d,e,g)}; 218 | f.la=function(a,b,c,d,e){return b===this.Ka?(a=Me(this.c,this.i,c),-1===a?(a=2*this.i,b=Array(a+2),Gc(this.c,0,b,0,a),b[a]=c,b[a+1]=d,e.qa=!0,new Ne(null,this.Ka,this.i+1,b)):O.b(this.c[a+1],d)?this:new Ne(null,this.Ka,this.i,xe(this.c,a+1,d))):(new Ae(null,1<<(this.Ka>>>a&31),[null,this])).la(a,b,c,d,e)};f.ja=function(){return new ze(this.c,0,null,null)}; 219 | function He(a){for(var b=[],c=arguments.length,d=0;;)if(dthis.end};bf.prototype.next=function(){var a=this.j;this.j+=this.step;return a}; 254 | function cf(a,b,c,d,e){this.l=a;this.start=b;this.end=c;this.step=d;this.m=e;this.h=32375006;this.A=8192}f=cf.prototype;f.toString=function(){return Cb(this)};f.equiv=function(a){return this.o(null,a)};f.indexOf=function(){var a=null,a=function(a,c){switch(arguments.length){case 1:return Q(this,a,0);case 2:return Q(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.a=function(a){return Q(this,a,0)};a.b=function(a,c){return Q(this,a,c)};return a}(); 255 | f.lastIndexOf=function(){function a(a){return S(this,a,R(this))}var b=null,b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return S(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.a=a;b.b=function(a,b){return S(this,a,b)};return b}();f.H=function(a,b){if(bthis.end&&0===this.step)return this.start;throw Error("Index out of bounds");}; 256 | f.Z=function(a,b,c){return bthis.end&&0===this.step?this.start:c};f.ja=function(){return new bf(this.start,this.end,this.step)};f.K=function(){return this.l};f.ca=function(){return 0this.end?new cf(this.l,this.start+this.step,this.end,this.step,null):null}; 257 | f.L=function(){return Da(this.G(null))?0:Math.ceil((this.end-this.start)/this.step)};f.I=function(){var a=this.m;return null!=a?a:this.m=a=ac(this)};f.o=function(a,b){return oc(this,b)};f.W=function(a,b){return ec(this,b)};f.X=function(a,b,c){for(a=this.start;;)if(0this.end)c=b.b?b.b(c,a):b.call(null,c,a),a+=this.step;else return c};f.$=function(){return null==this.G(null)?null:this.start}; 258 | f.da=function(){return null!=this.G(null)?new cf(this.l,this.start+this.step,this.end,this.step,null):Xb};f.G=function(){return 0this.step?this.start>this.end?this:null:this.start===this.end?null:this};f.N=function(a,b){return new cf(b,this.start,this.end,this.step,this.m)};f.S=function(a,b){return T(b,this)};cf.prototype[Fa]=function(){return Zb(this)}; 259 | function df(a,b,c,d,e,g,h){var k=za;za=null==za?null:za-1;try{if(null!=za&&0>za)return G(a,"#");G(a,c);if(0===Jb.a(g))J(h)&&G(a,function(){var a=ef.a(g);return v(a)?a:"..."}());else{if(J(h)){var l=L(h);b.g?b.g(l,a,g):b.call(null,l,a,g)}for(var m=N(h),n=Jb.a(g)-1;;)if(!m||null!=n&&0===n){J(m)&&0===n&&(G(a,d),G(a,function(){var a=ef.a(g);return v(a)?a:"..."}()));break}else{G(a,d);var p=L(m);c=a;h=g;b.g?b.g(p,c,h):b.call(null,p,c,h);var q=N(m);c=n-1;m=q;n=c}}return G(a,e)}finally{za=k}} 260 | function ff(a,b){var c=J(b);var d=null;for(var e=0,g=0;;)if(gb&&20>c){if(b=O.b(g,b)&&O.b(e,c))a=I.b(a,d+10),b=!O.b("black",a);h=!b}return h} 274 | function Gf(a,b,c){return rd(new Ze(null,new Db(null,1,[!0,null],null),null),function(){return function e(b){return new Yc(null,function(){for(;;){var g=J(b);if(g){if(Ec(g)){var k=wb(g),l=R(k),m=new $c(Array(l),0);a:for(var n=0;;)if(ng;var l=Lf(d),m=V(l,0,null),n=V(l,1,null);return v(Nf(d))?Rf(b,c):v(Hf(d,sf.a(e)))?setTimeout(function(g,h){return function(){var g=Mf(d,e),k=Df();return Tf.D?Tf.D(b,c,g,k,h):Tf.call(null,b,c,g,k,h)}}(h,k,g,l,m,n),5):setTimeout(function(g,h,k,l,m,n){return function(){var g=c+m*m,l=Kf(d,e,k);return Tf.D? 285 | Tf.D(b,g,n,l,h):Tf.call(null,b,g,n,l,h)}}(h,k,g,l,m,n),5)};function Uf(){var a=document.getElementById("canvas"),b=a.getContext("2d"),c=a.width;yd.b?yd.b(wf,c):yd.call(null,wf,c);a=a.height;yd.b?yd.b(xf,a):yd.call(null,xf,a);window.addEventListener("keydown",Of,!0);return Tf(b,0,Xd(Ad(200,Cd("black"))),Df(),(new Date).getTime())}var Vf=["tetris","core","init"],Wf=this;Vf[0]in Wf||!Wf.execScript||Wf.execScript("var "+Vf[0]); 286 | for(var Xf;Vf.length&&(Xf=Vf.shift());){var Yf;if(Yf=!Vf.length)Yf=void 0!==Uf;Yf?Wf[Xf]=Uf:Wf=Wf[Xf]?Wf[Xf]:Wf[Xf]={}}; 287 | })(); 288 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject tetris "0.1.0" 2 | :description "a simple Tetris game" 3 | :url "https://github.com/yogthos/Clojure-Tetris" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | 7 | :dependencies [[org.clojure/clojure "1.8.0"] 8 | [org.clojure/clojurescript "1.9.542"]] 9 | :plugins [[lein-cljsbuild "1.1.6"]] 10 | 11 | :main tetris.core 12 | 13 | :cljsbuild {:builds 14 | [{:compiler 15 | {:output-to "js/tetris.js" 16 | :optimizations :advanced 17 | :pretty-print false} 18 | :source-paths ["src"]}]}) 19 | -------------------------------------------------------------------------------- /src/tetris/core.clj: -------------------------------------------------------------------------------- 1 | (ns tetris.core 2 | (:use tetris.game) 3 | (:import 4 | (javax.swing JFrame) 5 | (java.awt Canvas Font Graphics Color Toolkit) 6 | (java.awt.event ActionListener KeyListener KeyEvent)) 7 | (:gen-class)) 8 | 9 | ;;;;;;Controls;;;; 10 | (defn handle-input [#^KeyEvent event] 11 | (condp = (.getKeyCode event) 12 | KeyEvent/VK_LEFT (swap! OFFSET #(map + [-1 0] %)) 13 | KeyEvent/VK_RIGHT (swap! OFFSET #(map + [1 0] %)) 14 | KeyEvent/VK_UP (reset! ROTATION :left) 15 | KeyEvent/VK_DOWN (reset! ROTATION :right))) 16 | 17 | (defn input-listener [] 18 | (proxy [ActionListener KeyListener] [] 19 | (actionPerformed [e]) 20 | (keyPressed [e] (handle-input e)) 21 | (keyReleased [e]) 22 | (keyTyped [e]))) 23 | 24 | ;;;;;;;UI;;;;;;;;; 25 | (def colors {"black" Color/black 26 | "blue" Color/blue 27 | "green" Color/green 28 | "yellow" Color/yellow 29 | "orange" Color/orange 30 | "pink" Color/pink 31 | "red" Color/red}) 32 | 33 | (defn draw [#^Canvas canvas draw-fn] 34 | (let [buffer (.getBufferStrategy canvas) 35 | g (.getDrawGraphics buffer)] 36 | (try 37 | (draw-fn g) 38 | 39 | (finally (.dispose g))) 40 | (if (not (.contentsLost buffer)) 41 | (. buffer show)) 42 | (.. Toolkit (getDefaultToolkit) (sync)))) 43 | 44 | (defn draw-square [x y color #^Graphics g] 45 | (let [width (/ @WIDTH COLS) 46 | height (/ @HEIGHT ROWS) 47 | xpos (* x width) 48 | ypos (* y width)] 49 | (doto g 50 | (.setColor (get colors color)) 51 | (.fillRect xpos ypos width height) 52 | (.setColor Color/black) 53 | (.drawRect xpos ypos width height)))) 54 | 55 | (defn draw-text [#^Graphics g color text x y] 56 | (doto g 57 | (.setColor color) 58 | (.drawString text x y))) 59 | 60 | (defn draw-game-over [score] 61 | (fn [#^Graphics g] 62 | (doto g 63 | (.setColor (new Color (float 0) (float 0) (float 0) (float 0.7))) 64 | (.fillRect 0 0 @WIDTH @HEIGHT)) 65 | (draw-text g Color/red "GAME OVER" (- (/ @WIDTH 2) 50) (/ @HEIGHT 2)) 66 | (draw-text g Color/red (str "Final Score: " score) (- (/ @WIDTH 2) 55) (+ 15 (/ @HEIGHT 2))))) 67 | 68 | (defn draw-board [board block score] 69 | (fn [#^Graphics g] 70 | (doto g 71 | (.setColor Color/BLACK) 72 | (.fillRect 0 0 @WIDTH @HEIGHT)) 73 | 74 | (doseq [square (range (count board))] 75 | (let [[x y] (pos-to-xy square)] 76 | (draw-square x y (get board square) g))) 77 | 78 | (doseq [[x y] (:shape block)] 79 | (draw-square x y (:color block) g)) 80 | 81 | (draw-text g Color/green (str "score: " score) 20 25))) 82 | 83 | (defn -main [& args] 84 | (reset! WIDTH 300) 85 | (reset! HEIGHT 600) 86 | (let [frame (JFrame. "Tetris") 87 | canvas (Canvas.)] 88 | (doto frame 89 | (.setSize @WIDTH (+ (/ @HEIGHT ROWS) @HEIGHT)) 90 | (.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE) 91 | (.setResizable false) 92 | (.add canvas) 93 | (.setVisible true)) 94 | 95 | (doto canvas 96 | (.createBufferStrategy 2) 97 | (.addKeyListener (input-listener)) 98 | (.setVisible true) 99 | (.requestFocus)) 100 | 101 | ;;game loop 102 | (loop [score 0 103 | board (get-board) 104 | block (get-block) 105 | old-time (System/currentTimeMillis)] 106 | 107 | (reset! OFFSET [0 0]) 108 | (reset! ROTATION nil) 109 | (Thread/sleep 10) 110 | (draw canvas (draw-board board block score)) 111 | 112 | (let [cur-time (System/currentTimeMillis) 113 | new-time (long (if (> (- cur-time old-time) 250) 114 | cur-time 115 | old-time)) 116 | drop? (> new-time old-time) 117 | [num-removed new-board] (clear-lines board)] 118 | (cond 119 | (game-over? board) 120 | (draw canvas (draw-game-over score)) 121 | 122 | (collides? board (:shape block)) 123 | (recur 124 | score 125 | (update-board board block) 126 | (get-block) 127 | new-time) 128 | 129 | :default 130 | (recur 131 | (+ score (* num-removed num-removed)) 132 | new-board 133 | (transform board block drop?) 134 | new-time)))))) 135 | 136 | ;(-main) 137 | -------------------------------------------------------------------------------- /src/tetris/core.cljs: -------------------------------------------------------------------------------- 1 | (ns tetris.core 2 | (:require [tetris.game :as game])) 3 | 4 | ;;Controls 5 | (defn key-down [evt] 6 | (condp = (.-keyCode evt) 7 | 37 (swap! game/OFFSET #(map + [-1 0] %)) 8 | 39 (swap! game/OFFSET #(map + [1 0] %)) 9 | 38 (reset! game/ROTATION :left) 10 | 40 (reset! game/ROTATION :right))) 11 | 12 | ;;UI 13 | (defn clear [ctx] 14 | (set! (.-fillStyle ctx) "black") 15 | (.fillRect ctx 0 0 @game/WIDTH @game/HEIGHT)) 16 | 17 | (defn draw-square [ctx color x y] 18 | (let [width (/ @game/WIDTH game/COLS) 19 | height (/ @game/HEIGHT game/ROWS) 20 | xpos (* x width) 21 | ypos (* y width)] 22 | 23 | (set! (.-fillStyle ctx) color) 24 | (.fillRect ctx xpos ypos width height) 25 | (set! (.-fillStyle ctx) "black") 26 | (.strokeRect ctx xpos ypos width height))) 27 | 28 | (defn draw-text [ctx color text x y] 29 | (set! (.-fillStyle ctx) color) 30 | (set! (.-font ctx) "20px Verdana") 31 | (.fillText ctx text x y)) 32 | 33 | (defn draw-game-over [ctx score] 34 | (set! (.-fillStyle ctx) "rgba(255, 255, 255, 0.5)") 35 | (.fillRect ctx 0 0 @game/WIDTH @game/HEIGHT) 36 | (draw-text ctx "red" "GAME OVER" (- (/ @game/WIDTH 2) 50) (/ @game/HEIGHT 2)) 37 | (draw-text ctx "red" (str "Final Score: " score) (- (/ @game/WIDTH 2) 55) (+ 15 (/ @game/HEIGHT 2)))) 38 | 39 | 40 | (defn draw-board [ctx board block score] 41 | (clear ctx) 42 | 43 | ;render the board 44 | (doseq [square (range (count board))] 45 | (let [[x y] (game/pos-to-xy square)] 46 | (draw-square ctx (get board square) x y))) 47 | 48 | ;draw the current block 49 | (doseq [[x y] (:shape block)] 50 | (draw-square ctx (:color block) x y)) 51 | 52 | (draw-text ctx "green" (str "score:" score) 20 25)) 53 | 54 | (declare game-loop) 55 | (defn game-loop [ctx score board block old-time] 56 | (reset! game/OFFSET [0 0]) 57 | (reset! game/ROTATION nil) 58 | 59 | (draw-board ctx board block score) 60 | 61 | (let [cur-time (.getTime (new js/Date)) 62 | new-time (if (> (- cur-time old-time) 250) 63 | cur-time 64 | old-time) 65 | drop? (> new-time old-time) 66 | [num-removed new-board] (game/clear-lines board)] 67 | 68 | (cond 69 | (game/game-over? board) 70 | (draw-game-over ctx score) 71 | 72 | (game/collides? board (:shape block)) 73 | (js/setTimeout 74 | (fn [] 75 | (game-loop ctx 76 | score 77 | (game/update-board board block) 78 | (game/get-block) 79 | new-time)) 80 | 5) 81 | 82 | :default 83 | (js/setTimeout 84 | (fn [] 85 | (game-loop ctx 86 | (+ score (* num-removed num-removed)) 87 | new-board 88 | (game/transform board block drop?) 89 | new-time)) 90 | 5)))) 91 | 92 | (defn ^:export init [] 93 | (let [canvas (.getElementById js/document "canvas") 94 | ctx (.getContext canvas "2d")] 95 | 96 | (reset! game/WIDTH (.-width canvas)) 97 | (reset! game/HEIGHT (.-height canvas)) 98 | 99 | (.addEventListener js/window "keydown" key-down true) 100 | (game-loop ctx 0 (game/get-board) (game/get-block) (.getTime (new js/Date))))) 101 | -------------------------------------------------------------------------------- /src/tetris/game.cljc: -------------------------------------------------------------------------------- 1 | (ns tetris.game) 2 | 3 | (def WIDTH (atom nil)) 4 | (def HEIGHT (atom nil)) 5 | 6 | (def COLS 10) 7 | (def ROWS 20) 8 | 9 | (def OFFSET (atom [0 0])) 10 | (def ROTATION (atom nil)) 11 | (def COLORS ["red" "blue" "green" "yellow" "orange" "pink"]) 12 | (def SHAPES [[[0 1] [0 2] [0 3] [0 4]] 13 | [[0 0] [0 1] [1 1] [1 2]] 14 | [[1 2] [1 1] [0 1] [0 0]] 15 | [[0 1] [1 1] [1 0] [2 1]] 16 | [[0 0] [0 1] [1 0] [1 1]] 17 | [[0 0] [0 1] [0 2] [1 2]] 18 | [[1 0] [1 1] [1 2] [0 2]]]) 19 | 20 | (defn get-block [] 21 | (let [shape (rand-nth SHAPES) 22 | offset (inc (rand-int (- COLS 3)))] 23 | {:color (rand-nth COLORS) 24 | :shape (map (fn [[x y]] [(+ x offset) y]) shape)})) 25 | 26 | (defn get-board [] 27 | (vec (take (* ROWS COLS) (repeat "black")))) 28 | 29 | (defn pos-to-xy [pos] 30 | (let [x (mod pos COLS) 31 | y (int (/ (- pos x) COLS))] 32 | [x, y])) 33 | 34 | 35 | (defn collides? 36 | ([board x y pos] 37 | (let [[posx posy] (pos-to-xy pos)] 38 | (and 39 | (> x (- 1)) 40 | (< x COLS) 41 | (< y ROWS) 42 | (not (and 43 | (= posx x) 44 | (= posy y) 45 | (not= "black" (get board (+ pos COLS)))))))) 46 | ([board shape pos] 47 | (every? 48 | #{true} 49 | (for [[x y] shape] 50 | (collides? board x y pos)))) 51 | ([board shape] 52 | (not (reduce 53 | #(and %1 (collides? board shape %2)) 54 | (range (count board)))))) 55 | 56 | (defn rotate [board shape] 57 | (if @ROTATION 58 | (let [[avg-x avg-y] (->> shape 59 | (reduce 60 | (fn [[tx ty] [x y]] 61 | [(+ tx x) (+ ty y)])) 62 | (map #(int (/ % 4)))) 63 | 64 | rotated (map (fn [[x y]] 65 | [(int (+ avg-x (- y avg-y))) 66 | (int (- avg-y (- x avg-x)))]) 67 | shape)] 68 | (if (collides? board rotated) 69 | shape rotated)) 70 | shape)) 71 | 72 | (defn shift [board shape] 73 | (let [shifted (map 74 | (fn [[x y]] 75 | [(+ x (first @OFFSET)) y]) 76 | shape)] 77 | (if (collides? board shifted) 78 | shape shifted))) 79 | 80 | (defn transform [board {:keys [color shape]} drop?] 81 | (let [rotated (->> shape (shift board) (rotate board))] 82 | {:color color 83 | :shape (if drop? 84 | (map (fn [[x y]] [x (inc y)]) rotated) 85 | rotated)})) 86 | 87 | (defn clear-lines [board] 88 | (let [new-board (->> board 89 | (partition COLS) 90 | (filter #(some #{"black"} %)) 91 | (apply concat)) 92 | num-removed (- (count board) (count new-board))] 93 | [num-removed 94 | (into (vec (take num-removed (repeat "black"))) 95 | new-board)])) 96 | 97 | (defn update-board [board {:keys [color shape]}] 98 | (vec (map #(let [[x y] (pos-to-xy %)] 99 | (if (some (fn [[px py]] (and (= x px) (= y py))) 100 | shape) 101 | color (get board %))) 102 | (range (count board))))) 103 | 104 | (defn game-over? [board] 105 | (not (reduce #(and %1 (= "black" %2)) 106 | (butlast (rest (take COLS board)))))) 107 | -------------------------------------------------------------------------------- /tetris-1.0-standalone.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yogthos/clj-tetris/6c66f6337da4d09bcbf0f0937ae1cf2475dda0a6/tetris-1.0-standalone.jar -------------------------------------------------------------------------------- /tetris.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Canvas Test 6 | 7 | 8 | 9 |
10 |
11 | 12 | This text is displayed if your browser does not support HTML5 Canvas. 13 | 14 |
15 | 16 |
17 | 20 | 21 | --------------------------------------------------------------------------------