diff --git a/LICENSE b/LICENSE index 7d4b906..a02a0ed 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,5 @@ Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jake Gordon and contributors +Copyright (c) 2017, 2018 Stasinos Konstantopoulos Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 9b2720e..6b08d28 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,56 @@ -Javascript Tetris -================= +Frustrating Tetris +================== -An HTML5 Tetris Game +An HTML5 Frustrating Tetris Game +forked from https://github.com/jakesgordon/javascript-tetris - * [play the game](http://codeincomplete.com/projects/tetris/) - * read a [blog article](http://codeincomplete.com/posts/2011/10/10/javascript_tetris/) - * view the [source](https://github.com/jakesgordon/javascript-tetris) +Added features: ->> _*SUPPORTED BROWSERS*: Chrome, Firefox, Safari, Opera and IE9+_ +1. There is a probability that the current brick is dropped regardless +of what the user action was. This probability increases as the pit +gets filled up, so that the game is more likely to ignore your action +when the game is at a critical stage. -FUTURE -====== +2. There is a probability that a random number (1--7) of IGNORE +actions are added to the action queue, eating up as many keystrokes +without rotating or moving the falling brick. - * menu - * animation and fx - * levels - * high scores - * touch support - * music and sound fx +Both features only kick in once there are 8 or fewer empty rows at the +top ofg the pit, and the probability that they appear increases as +fewer rows are left empty. This would normally result in minor hints +that there is something wrong as the game advances, with heightened +frequency when players enter a critical phase with little space left. +Frustration Parameters +====================== + + - Player frustration is only active when so many or fewer empty rows: + (l.86) frStart = 8 + + - The probability to apply frustration (if active, as per above) is: + (l. 455) screwPlayer = 1.0 / nEMPTY + where nEMPTY is the number of empty rows + If this kicks in, they two types are equally probable. + + In order to make one more likely, we need to : + * Have different "screwPlayer" variables initialized in l. 94 + * Calculate differently in l. 455 + * Use different variable to activate each type of frustration + in l. 225 (ignore) and l. 238 (drop to bottom) + + - If "dead key" is chosen, between 1 and 8 "ignores" are pushed into + the action queue: + (l. 229) n = Math.random(1, 8) + Each ignore actions needs one keystroke to be + shifted out, eating up the actual action. + + - If "drop to bottom" is chosen, the current brick will fall all the way + to the bottom, regardless of what action was choses. No parameters here. + + License ======= [MIT](http://en.wikipedia.org/wiki/MIT_License) license. - diff --git a/index.html b/index.html index 37d4af0..75dafc9 100644 --- a/index.html +++ b/index.html @@ -32,6 +32,7 @@

score 00000

rows 0

+ Sorry, this example cannot be run because your browser does not support the <canvas> element @@ -68,23 +69,30 @@ // game constants //------------------------------------------------------------------------- - var KEY = { ESC: 27, SPACE: 32, LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40 }, - DIR = { UP: 0, RIGHT: 1, DOWN: 2, LEFT: 3, MIN: 0, MAX: 3 }, + var KEY = { ESC: 27, SPACE: 32, LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40, SHIFT: 16, P: 80 }, + // drop all the way to BOTTOM is an action + // IGNORE subsequent actions is also an action + // BUT: MAX=3 to avoid reaching these two pseudo-actions by rotation + DIR = { UP: 0, RIGHT: 1, DOWN: 2, LEFT: 3, BOTTOM:4, IGNORE:5, MIN: 0, MAX: 3 }, stats = new Stats(), canvas = get('canvas'), ctx = canvas.getContext('2d'), ucanvas = get('upcoming'), uctx = ucanvas.getContext('2d'), speed = { start: 0.6, decrement: 0.005, min: 0.1 }, // how long before piece drops by 1 row (seconds) + paused = 0, nx = 10, // width of tetris court (in blocks) ny = 20, // height of tetris court (in blocks) - nu = 5; // width/height of upcoming preview (in blocks) + nu = 5, // width/height of upcoming preview (in blocks) + frStart = 8; // player frustration active when 8 or fewer empty rows //------------------------------------------------------------------------- // game variables (initialized during reset) //------------------------------------------------------------------------- var dx, dy, // pixel size of a single tetris block + nEMPTY = ny, // court lines that are totaly empty + screwPlayer = 0.0, // current probability to screw player blocks, // 2 dimensional array (nx*ny) representing tetris court - either empty block or occupied by a 'piece' actions, // queue of user actions (inputs) playing, // true|false - game is in progress @@ -215,13 +223,57 @@ function keydown(ev) { var handled = false; if (playing) { - switch(ev.keyCode) { - case KEY.LEFT: actions.push(DIR.LEFT); handled = true; break; - case KEY.RIGHT: actions.push(DIR.RIGHT); handled = true; break; - case KEY.UP: actions.push(DIR.UP); handled = true; break; - case KEY.DOWN: actions.push(DIR.DOWN); handled = true; break; - case KEY.ESC: lose(); handled = true; break; + if( (nEMPTY<=frStart) && (Math.random()0 ; --n ) { + actions.push( DIR.IGNORE ); + } + } + if( ev.keyCode == KEY.ESC ) { + lose(); handled = true; + } + else if( (nEMPTY<=frStart) && (Math.random()0) && (actions[0]==DIR.IGNORE) ) { + //document.getElementById("debugfield").innerHTML = "DIR.IGNORE"; + actions.shift(); + handled = true; + } + else { + //document.getElementById("debugfield").innerHTML = "NOT DIR.IGNORE"; + switch(ev.keyCode) { + case KEY.LEFT: actions.push(DIR.LEFT); handled = true; break; + case KEY.RIGHT: actions.push(DIR.RIGHT); handled = true; break; + case KEY.UP: actions.push(DIR.UP); handled = true; break; + case KEY.DOWN: actions.push(DIR.DOWN); handled = true; break; + case KEY.SHIFT: + // special care to only push once, even if it stays pressed + if( actions[0] != DIR.BOTTOM ) { + actions.push(DIR.BOTTOM); handled = true; + } + break; + case KEY.ESC: lose(); handled = true; break; + case KEY.P: + if( paused == 1 ) { + paused = 0; + } + else { + paused = 1; + } + handled = true; + break; + } + } + } } else if (ev.keyCode == KEY.SPACE) { play(); @@ -257,6 +309,7 @@ clearActions(); clearBlocks(); clearRows(); + nEMPTY = ny, clearScore(); setCurrentPiece(next); setNextPiece(); @@ -266,7 +319,7 @@ if (playing) { if (vscore < score) setVisualScore(vscore + 1); - handle(actions.shift()); + handle( actions.shift() ); dt = dt + idt; if (dt > step) { dt = dt - step; @@ -277,10 +330,11 @@ function handle(action) { switch(action) { - case DIR.LEFT: move(DIR.LEFT); break; - case DIR.RIGHT: move(DIR.RIGHT); break; - case DIR.UP: rotate(); break; - case DIR.DOWN: drop(); break; + case DIR.LEFT: move(DIR.LEFT); break; + case DIR.RIGHT: move(DIR.RIGHT); break; + case DIR.UP: rotate(); break; + case DIR.DOWN: drop(); break; + case DIR.BOTTOM: dropMany(); break; } } @@ -311,6 +365,9 @@ } function drop() { + if( paused == 0 ) { + console.log( "0" ); + if (!move(DIR.DOWN)) { addScore(10); dropPiece(); @@ -322,6 +379,20 @@ lose(); } } + } // paused + } + + function dropMany() { + while( move(DIR.DOWN) ) { } + addScore(10); + dropPiece(); + removeLines(); + setCurrentPiece(next); + setNextPiece(randomPiece()); + clearActions(); + if (occupied(current.type, current.x, current.y, current.dir)) { + lose(); + } } function dropPiece() { @@ -387,9 +458,17 @@ drawPiece(ctx, current.type, current.x, current.y, current.dir); var x, y, block; for(y = 0 ; y < ny ; y++) { + empty_line = true for (x = 0 ; x < nx ; x++) { - if (block = getBlock(x,y)) + if (block = getBlock(x,y)) { drawBlock(ctx, x, y, block.color); + empty_line = false + } + } + if( empty_line ) { + nEMPTY = y + screwPlayer = 1.0 / nEMPTY + //document.getElementById("debugfield").innerHTML = nEMPTY.toString() + " : " + screwPlayer.toString(); } } ctx.strokeRect(0, 0, nx*dx - 1, ny*dy - 1); // court boundary