UNPKG

match3game-toembal

Version:

Match3Game is an engaging and colorful match-3 puzzle game.

466 lines (413 loc) 11.1 kB
class Game { constructor(size, typesOfTiles) { this.size = size; this.typesOfTiles = typesOfTiles; this.board = []; this.score = 0; this.initBoard(); } initBoard() { for (let i = 0; i < this.size; i++) { this.board[i] = []; for (let j = 0; j < this.size; j++) { this.board[i][j] = this.randomTile(); } } } randomTile() { return Math.floor(Math.random() * this.typesOfTiles); } swapTiles(x1, y1, x2, y2) { let temp = this.board[x1][y1]; this.board[x1][y1] = this.board[x2][y2]; this.board[x2][y2] = temp; this.checkForMatches(); } checkForMatches() { let matches = []; for (let i = 0; i < this.size; i++) { for (let j = 0; j < this.size; j++) { if (this.isMatch(i, j)) { matches.push({x: i, y: j}); } } } if (matches.length > 0) { this.removeMatches(matches); this.score += matches.length; this.fillSpaces(); this.checkForMatches(); } } isMatch(x, y) { let tile = this.board[x][y]; return this.checkDirection(x, y, -1, 0, tile) || this.checkDirection(x, y, 1, 0, tile) || this.checkDirection(x, y, 0, -1, tile) || this.checkDirection(x, y, 0, 1, tile); } checkDirection(x, y, dx, dy, tile) { let count = 1; let nx = x + dx; let ny = y + dy; while (nx >= 0 && nx < this.size && ny >= 0 && ny < this.size && this.board[nx][ny] === tile) { count++; nx += dx; ny += dy; } return count >= 3; } removeMatches(matches) { matches.forEach(match => { this.board[match.x][match.y] = this.randomTile(); }); } fillSpaces() { for (let i = 0; i < this.size; i++) { for (let j = 0; j < this.size; j++) { if (this.board[i][j] === null) { this.board[i][j] = this.randomTile(); } } } } } let game = new Game(8, 5); const Tween = { linear: function (t, b, c, d) { return (c * t) / d + b; }, easeIn: function (t, b, c, d) { return c * (t /= d) * t + b; }, easeOut: function (t, b, c, d) { return -c * (t /= d) * (t - 2) + b; }, easeBoth: function (t, b, c, d) { if ((t /= d / 2) < 1) { return (c / 2) * t * t + b; } return (-c / 2) * (--t * (t - 2) - 1) + b; }, easeInStrong: function (t, b, c, d) { return c * (t /= d) * t * t * t + b; }, easeOutStrong: function (t, b, c, d) { return -c * ((t = t / d - 1) * t * t * t - 1) + b; }, easeBothStrong: function (t, b, c, d) { if ((t /= d / 2) < 1) { return (c / 2) * t * t * t * t + b; } return (-c / 2) * ((t -= 2) * t * t * t - 2) + b; }, elasticIn: function (t, b, c, d, a, p) { if (t === 0) { return b; } if ((t /= d) == 1) { return b + c; } if (!p) { p = d * 0.3; } if (!a || a < Math.abs(c)) { a = c; var s = p / 4; } else { var s = (p / (2 * Math.PI)) * Math.asin(c / a); } return ( -( a * Math.pow(2, 10 * (t -= 1)) * Math.sin(((t * d - s) * (2 * Math.PI)) / p) ) + b ); }, elasticOut: function (t, b, c, d, a, p) { if (t === 0) { return b; } if ((t /= d) == 1) { return b + c; } if (!p) { p = d * 0.3; } if (!a || a < Math.abs(c)) { a = c; var s = p / 4; } else { var s = (p / (2 * Math.PI)) * Math.asin(c / a); } return ( a * Math.pow(2, -10 * t) * Math.sin(((t * d - s) * (2 * Math.PI)) / p) + c + b ); }, elasticBoth: function (t, b, c, d, a, p) { if (t === 0) { return b; } if ((t /= d / 2) == 2) { return b + c; } if (!p) { p = d * (0.3 * 1.5); } if (!a || a < Math.abs(c)) { a = c; var s = p / 4; } else { var s = (p / (2 * Math.PI)) * Math.asin(c / a); } if (t < 1) { return ( -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin(((t * d - s) * (2 * Math.PI)) / p)) + b ); } return ( a * Math.pow(2, -10 * (t -= 1)) * Math.sin(((t * d - s) * (2 * Math.PI)) / p) * 0.5 + c + b ); }, backIn: function (t, b, c, d, s) { if (typeof s == "undefined") { s = 1.70158; } return c * (t /= d) * t * ((s + 1) * t - s) + b; }, backOut: function (t, b, c, d, s) { if (typeof s == "undefined") { s = 1.70158; } return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b; }, backBoth: function (t, b, c, d, s) { if (typeof s == "undefined") { s = 1.70158; } if ((t /= d / 2) < 1) { return (c / 2) * (t * t * (((s *= 1.525) + 1) * t - s)) + b; } return (c / 2) * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2) + b; }, bounceIn: function (t, b, c, d) { return c - Tween["bounceOut"](d - t, 0, c, d) + b; }, bounceOut: function (t, b, c, d) { //* if ((t /= d) < 1 / 2.75) { return c * (7.5625 * t * t) + b; } else if (t < 2 / 2.75) { return c * (7.5625 * (t -= 1.5 / 2.75) * t + 0.75) + b; } else if (t < 2.5 / 2.75) { return c * (7.5625 * (t -= 2.25 / 2.75) * t + 0.9375) + b; } return c * (7.5625 * (t -= 2.625 / 2.75) * t + 0.984375) + b; }, bounceBoth: function (t, b, c, d) { if (t < d / 2) { return Tween["bounceIn"](t * 2, 0, c, d) * 0.5 + b; } return Tween["bounceOut"](t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b; }, }; const transformAttrs = [ // 'matrix', //(1.0, 2.0, 3.0, 4.0, 5.0, 6.0) // 'matrix3d', //(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0) // 'perspective', //(17px) "rotate", //(0.5turn) // 'rotate3d', //(1, 2.0, 3.0, 10deg) "rotateX", //(10deg) "rotateY", //(10deg) "rotateZ", //(10deg) "scale", //(2, 0.5) // 'scale3d', //(2.5, 1.2, 0.3) "scaleX", //(2) "scaleY", //(0.5) "scaleZ", //(0.3) "skew", //(30deg, 20deg) "skewX", //(30deg) "skewY", //(1.07rad) "translate", //(12px, 50%) // 'translate3d', //(12px, 50%, 3em) "translateX", //(2em) "translateY", //(3in) "translateZ", //(2px) ]; const numericalAttrs = [ "left", "right", "top", "bottom", "width", "height", "paddingTop", "paddingBottom", "paddingLeft", "paddingRight", "marginTop", "marginBottom", "marginLeft", "marginRight", ]; function transformCSS(el, attr, value) { el.transformData = el.transformData || {}; if (value === undefined) { let result = el.transformData[attr]; if (result === undefined) { if (["scale", "scaleX", "scaleY"].includes(attr)) { result = 1; } else { result = 0; } } return result; } let transformStr = ""; el.transformData[attr] = value; for (let i in el.transformData) { switch (i) { case "translate": case "translateX": case "translateY": case "translateZ": transformStr += i + `(${el.transformData[i]}px) `; break; case "scale": case "scaleX": case "scaleY": case "scaleZ": transformStr += i + `(${el.transformData[i]}px) `; break; case "rotate": case "rotatX": case "rotatY": case "rotatZ": case "skew": case "skewX": case "skewY": transformStr += i + `(${el.transformData[i]}deg) `; break; } el.style.transform = transformStr.trim(); } } function css(el, attr, value) { if (transformAttrs.includes(attr)) { return transformCSS(el, attr, value); } if (value === undefined && typeof attr !== "object") { let result = getComputedStyle(el)[attr]; if (numericalAttrs.includes(result) || !isNaN(parseFloat(result))) { result = parseFloat(result); } return result; } else { if (typeof attr === "object") { for (key in attr) { css(el, key, attr[key]); } } else if (attr === "opacity") { el.style.opacity = value; } else if (numericalAttrs.includes(attr)) { el.style[attr] = value + "px"; } else if (attr === "zIndex") { el.style[attr] = parseInt(value); } else { el.style[attr] = value; } } } function mTween(opts) { let { el, attr, duration = 500, fx = "easeOut" } = opts; if (el.timer) return; let maxC = 0; if (typeof duration === "object") { let durationOpt = duration; duration.multiple = durationOpt.multiple || 2; duration = maxC * duration.multiple; duration = durationOpt.max ? Math.min(duration, durationOpt.max) : duration; duration = durationOpt.min ? Math.max(duration, durationOpt.min) : duration; } let t = 0; let b = {}; let c = {}; let d = Math.ceil(duration / (1000 / 60)); for (key in attr) { b[key] = css(el, key); c[key] = attr[key] - b[key]; maxC = Math.max(maxC, Math.abs(c[key])); } move(); function move() { el.timer = requestAnimationFrame(() => { t++; if (t > d) { el.timer = null; opts.cb && opts.cb(); } else { for (key in attr) { let value = Tween[fx](t, b[key], c[key], d); css(el, key, value); } move(); } }); } } mTween.stop = function (el) { cancelAnimationFrame(el.timer); el.timer = null; }; function shake(opts) { let { el, attr, shakeLength = 15 } = opts; let shakeArr = []; el.shakeStart = {}; if (el.shake) { return; } if (typeof attr === "object") { for (let i = 0; i < attr.length; i++) { el.shakeStart[attr[i]] = css(el, attr[i]); } } else { el.shakeStart[attr] = css(el, attr); } for (let i = shakeLength; i >= 0; i--) { shakeArr.push(i % 2 ? i : -i); } function move() { el.shake = requestAnimationFrame(function () { if (shakeArr.length <= 0) { el.shake = false; opts.cb && opts.cb(); } else { let num = shakeArr.shift(); for (let s in el.shakeStart) { css(el, s, el.shakeStart[s] + num); } move(); } }); } move(); } shake.stop = function (el) { cancelAnimationFrame(el.shake); el.shake = false; for (let s in el.shakeStart) { css(el, s, el.shakeStart[s]); } };