2048是一款有趣的益智小游戏,传说只有一开始就再也停不下来,真的有这么大的吸引力吗?赶紧进入游戏挑战试试吧,看你能否成功将数字组合到2048,很有难度的小游戏。

将以下代码另存为html文件,使用浏览器打开即可运行。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html lang="en">

<head>

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    <meta charset="utf-8">

    <title>2048小游戏-xarjtc.com</title>

    <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0, maximum-scale=1, user-scalable=no, minimal-ui">

      <style>	    html, body {

    margin: 0;

    padding: 0;

    background: #faf8ef;

    color: #776e65;

    font-family: "Microsoft YaHei", sans-serif, 'Microsoft Sans Serif','Microsoft JhengHei UI';

    font-size: 18px;

}

 

body {

    margin: 80px 0;

}

 

input {

    display: inline-block;

    background: #8f7a66;

    border-radius: 3px;

    padding: 0 20px;

    text-decoration: none;

    color: #f9f6f2;

    height: 40px;

    line-height: 42px;

    cursor: pointer;

    font: inherit;

    border: none;

    outline: none;

    box-sizing: border-box;

    font-weight: bold;

    margin: 0;

    -webkit-appearance: none;

    -moz-appearance: none;

    appearance: none;

}

 

input[type="text"], input[type="email"] {

    cursor: auto;

    background: #fcfbf9;

    font-weight: normal;

    color: #776e65;

    padding: 0 15px;

}

 

input[type="text"]::-webkit-input-placeholder, input[type="email"]::-webkit-input-placeholder {

    color: #9d948c;

}

 

input[type="text"]::-moz-placeholder, input[type="email"]::-moz-placeholder {

    color: #9d948c;

}

 

input[type="text"]:-ms-input-placeholder, input[type="email"]:-ms-input-placeholder {

    color: #9d948c;

}

 

.heading:after {

    content: "";

    display: block;

    clear: both;

}

 

h1.title {

    font-size: 80px;

    font-weight: bold;

    margin: 0;

    display: block;

    float: left;

}

 

@-webkit-keyframes move-up {

    0% {

        top: 25px;

        opacity: 1;

    }

 

    100% {

        top: -50px;

        opacity: 0;

    }

}

 

@-moz-keyframes move-up {

    0% {

        top: 25px;

        opacity: 1;

    }

 

    100% {

        top: -50px;

        opacity: 0;

    }

}

 

@keyframes move-up {

    0% {

        top: 25px;

        opacity: 1;

    }

 

    100% {

        top: -50px;

        opacity: 0;

    }

}

 

.scores-container {

    float: right;

    text-align: right;

}

 

.score-container, .best-container {

    position: relative;

    display: inline-block;

    background: #bbada0;

    padding: 15px 25px;

    font-size: 25px;

    height: 25px;

    line-height: 47px;

    font-weight: bold;

    border-radius: 3px;

    color: white;

    margin-top: 8px;

    text-align: center;

}

 

.score-container:after, .best-container:after {

    position: absolute;

    width: 100%;

    top: 10px;

    left: 0;

    text-transform: uppercase;

    font-size: 13px;

    line-height: 13px;

    text-align: center;

    color: #eee4da;

}

 

.score-container .score-addition, .best-container .score-addition {

    position: absolute;

    right: 30px;

    color: red;

    font-size: 25px;

    line-height: 25px;

    font-weight: bold;

    color: rgba(119, 110, 101, 0.9);

    z-index: 100;

    -webkit-animation: move-up 600ms ease-in;

    -moz-animation: move-up 600ms ease-in;

    animation: move-up 600ms ease-in;

    -webkit-animation-fill-mode: both;

    -moz-animation-fill-mode: both;

    animation-fill-mode: both;

}

 

.score-container:after {

    content: "Score";

}

 

.best-container:after {

    content: "Best";

}

 

p {

    margin-top: 0;

    margin-bottom: 10px;

    line-height: 1.65;

}

 

a {

    color: #776e65;

    font-weight: bold;

    text-decoration: underline;

    cursor: pointer;

}

 

strong.important {

    text-transform: uppercase;

}

 

hr {

    border: none;

    border-bottom: 1px solid #d8d4d0;

    margin-top: 20px;

    margin-bottom: 30px;

}

 

.container {

    width: 500px;

    margin: 0 auto;

}

 

@-webkit-keyframes fade-in {

    0% {

        opacity: 0;

    }

 

    100% {

        opacity: 1;

    }

}

 

@-moz-keyframes fade-in {

    0% {

        opacity: 0;

    }

 

    100% {

        opacity: 1;

    }

}

 

@keyframes fade-in {

    0% {

        opacity: 0;

    }

 

    100% {

        opacity: 1;

    }

}

 

@-webkit-keyframes slide-up {

    0% {

        margin-top: 32%;

    }

 

    100% {

        margin-top: 20%;

    }

}

 

@-moz-keyframes slide-up {

    0% {

        margin-top: 32%;

    }

 

    100% {

        margin-top: 20%;

    }

}

 

@keyframes slide-up {

    0% {

        margin-top: 32%;

    }

 

    100% {

        margin-top: 20%;

    }

}

 

.game-container {

    margin-top: 40px;

    position: relative;

    padding: 15px;

    cursor: default;

    -webkit-touch-callout: none;

    -ms-touch-callout: none;

    -webkit-user-select: none;

    -moz-user-select: none;

    -ms-user-select: none;

    -ms-touch-action: none;

    touch-action: none;

    background: #bbada0;

    border-radius: 6px;

    width: 500px;

    height: 500px;

    -webkit-box-sizing: border-box;

    -moz-box-sizing: border-box;

    box-sizing: border-box;

}

 

.game-message {

    display: none;

    position: absolute;

    top: 0;

    right: 0;

    bottom: 0;

    left: 0;

    background: rgba(238, 228, 218, 0.73);

    z-index: 100;

    padding-top: 40px;

    text-align: center;

    -webkit-animation: fade-in 800ms ease 1200ms;

    -moz-animation: fade-in 800ms ease 1200ms;

    animation: fade-in 800ms ease 1200ms;

    -webkit-animation-fill-mode: both;

    -moz-animation-fill-mode: both;

    animation-fill-mode: both;

}

 

.game-message p {

    font-size: 60px;

    font-weight: bold;

    height: 60px;

    line-height: 60px;

    margin-top: 222px;

}

 

.game-message .lower {

    display: block;

    margin-top: 29px;

}

 

.game-message .mailing-list {

    margin-top: 52px;

}

 

.game-message .mailing-list strong {

    display: block;

    margin-bottom: 10px;

}

 

.game-message .mailing-list .mailing-list-email-field {

    width: 230px;

    margin-right: 5px;

}

 

.game-message a {

    display: inline-block;

    background: #8f7a66;

    border-radius: 3px;

    padding: 0 20px;

    text-decoration: none;

    color: #f9f6f2;

    height: 40px;

    line-height: 42px;

    cursor: pointer;

    margin-left: 9px;

}

 

.game-message a.keep-playing-button {

    display: none;

}

 

.game-message .score-sharing {

    display: inline-block;

    vertical-align: middle;

    margin-left: 10px;

}

 

.game-message.game-won {

    background: rgba(237, 194, 46, 0.5);

    color: #f9f6f2;

}

 

.game-message.game-won a.keep-playing-button {

    display: inline-block;

}

 

.game-message.game-won, .game-message.game-over {

    display: block;

}

 

.game-message.game-won p, .game-message.game-over p {

    -webkit-animation: slide-up 1.5s ease-in-out 2500ms;

    -moz-animation: slide-up 1.5s ease-in-out 2500ms;

    animation: slide-up 1.5s ease-in-out 2500ms;

    -webkit-animation-fill-mode: both;

    -moz-animation-fill-mode: both;

    animation-fill-mode: both;

}

 

.game-message.game-won .mailing-list, .game-message.game-over .mailing-list {

    -webkit-animation: fade-in 1.5s ease-in-out 2500ms;

    -moz-animation: fade-in 1.5s ease-in-out 2500ms;

    animation: fade-in 1.5s ease-in-out 2500ms;

    -webkit-animation-fill-mode: both;

    -moz-animation-fill-mode: both;

    animation-fill-mode: both;

}

 

.grid-container {

    position: absolute;

    z-index: 1;

}

 

.grid-row {

    margin-bottom: 15px;

}

 

.grid-row:last-child {

    margin-bottom: 0;

}

 

.grid-row:after {

    content: "";

    display: block;

    clear: both;

}

 

.grid-cell {

    width: 106.25px;

    height: 106.25px;

    margin-right: 15px;

    float: left;

    border-radius: 3px;

    background: rgba(238, 228, 218, 0.35);

}

 

.grid-cell:last-child {

    margin-right: 0;

}

 

.tile-container {

    position: absolute;

    z-index: 2;

}

 

.tile, .tile .tile-inner {

    width: 107px;

    height: 107px;

    line-height: 116.25px;

}

 

.tile.tile-position-1-1 {

    -webkit-transform: translate(0px, 0px);

    -moz-transform: translate(0px, 0px);

    transform: translate(0px, 0px);

}

 

.tile.tile-position-1-2 {

    -webkit-transform: translate(0px, 121px);

    -moz-transform: translate(0px, 121px);

    transform: translate(0px, 121px);

}

 

.tile.tile-position-1-3 {

    -webkit-transform: translate(0px, 242px);

    -moz-transform: translate(0px, 242px);

    transform: translate(0px, 242px);

}

 

.tile.tile-position-1-4 {

    -webkit-transform: translate(0px, 363px);

    -moz-transform: translate(0px, 363px);

    transform: translate(0px, 363px);

}

 

.tile.tile-position-2-1 {

    -webkit-transform: translate(121px, 0px);

    -moz-transform: translate(121px, 0px);

    transform: translate(121px, 0px);

}

 

.tile.tile-position-2-2 {

    -webkit-transform: translate(121px, 121px);

    -moz-transform: translate(121px, 121px);

    transform: translate(121px, 121px);

}

 

.tile.tile-position-2-3 {

    -webkit-transform: translate(121px, 242px);

    -moz-transform: translate(121px, 242px);

    transform: translate(121px, 242px);

}

 

.tile.tile-position-2-4 {

    -webkit-transform: translate(121px, 363px);

    -moz-transform: translate(121px, 363px);

    transform: translate(121px, 363px);

}

 

.tile.tile-position-3-1 {

    -webkit-transform: translate(242px, 0px);

    -moz-transform: translate(242px, 0px);

    transform: translate(242px, 0px);

}

 

.tile.tile-position-3-2 {

    -webkit-transform: translate(242px, 121px);

    -moz-transform: translate(242px, 121px);

    transform: translate(242px, 121px);

}

 

.tile.tile-position-3-3 {

    -webkit-transform: translate(242px, 242px);

    -moz-transform: translate(242px, 242px);

    transform: translate(242px, 242px);

}

 

.tile.tile-position-3-4 {

    -webkit-transform: translate(242px, 363px);

    -moz-transform: translate(242px, 363px);

    transform: translate(242px, 363px);

}

 

.tile.tile-position-4-1 {

    -webkit-transform: translate(363px, 0px);

    -moz-transform: translate(363px, 0px);

    transform: translate(363px, 0px);

}

 

.tile.tile-position-4-2 {

    -webkit-transform: translate(363px, 121px);

    -moz-transform: translate(363px, 121px);

    transform: translate(363px, 121px);

}

 

.tile.tile-position-4-3 {

    -webkit-transform: translate(363px, 242px);

    -moz-transform: translate(363px, 242px);

    transform: translate(363px, 242px);

}

 

.tile.tile-position-4-4 {

    -webkit-transform: translate(363px, 363px);

    -moz-transform: translate(363px, 363px);

    transform: translate(363px, 363px);

}

 

.tile {

    position: absolute;

    -webkit-transition: 100ms ease-in-out;

    -moz-transition: 100ms ease-in-out;

    transition: 100ms ease-in-out;

    -webkit-transition-property: -webkit-transform;

    -moz-transition-property: -moz-transform;

    transition-property: transform;

}

 

.tile .tile-inner {

    border-radius: 3px;

    background: #eee4da;

    text-align: center;

    font-weight: bold;

    z-index: 10;

    font-size: 40px;

}

 

.tile.tile-2 .tile-inner {

    background: #eee4da;

    box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0), inset 0 0 0 1px rgba(255, 255, 255, 0);

}

 

.tile.tile-4 .tile-inner {

    background: #ede0c8;

    box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0), inset 0 0 0 1px rgba(255, 255, 255, 0);

}

 

.tile.tile-8 .tile-inner {

    color: #f9f6f2;

    background: #f2b179;

}

 

.tile.tile-16 .tile-inner {

    color: #f9f6f2;

    background: #f59563;

}

 

.tile.tile-32 .tile-inner {

    color: #f9f6f2;

    background: #f67c5f;

}

 

.tile.tile-64 .tile-inner {

    color: #f9f6f2;

    background: #f65e3b;

    font-size: 30px;

}

 

@media screen and (max-width: 520px) {

    .tile.tile-64 .tile-inner {

        font-size: 17px;

    }

}

 

.tile.tile-128 .tile-inner {

    color: #f9f6f2;

    background: #edcf72;

    box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.2381), inset 0 0 0 1px rgba(255, 255, 255, 0.14286);

    font-size: 25px;

}

 

@media screen and (max-width: 520px) {

    .tile.tile-128 .tile-inner {

        font-size: 13px;

    }

}

 

.tile.tile-256 .tile-inner {

    color: #f9f6f2;

    background: #edcc61;

    box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.31746), inset 0 0 0 1px rgba(255, 255, 255, 0.19048);

    font-size: 30px;

}

 

@media screen and (max-width: 520px) {

    .tile.tile-256 .tile-inner {

        font-size: 17px;

    }

}

 

.tile.tile-512 .tile-inner {

    color: #f9f6f2;

    background: #edc850;

    box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.39683), inset 0 0 0 1px rgba(255, 255, 255, 0.2381);

    font-size: 30px;

}

 

@media screen and (max-width: 520px) {

    .tile.tile-512 .tile-inner {

        font-size: 17px;

    }

}

 

.tile.tile-1024 .tile-inner {

    color: #f9f6f2;

    background: #edc53f;

    box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.47619), inset 0 0 0 1px rgba(255, 255, 255, 0.28571);

    font-size: 30px;

}

 

@media screen and (max-width: 520px) {

    .tile.tile-1024 .tile-inner {

        font-size: 17px;

    }

}

 

.tile.tile-2048 .tile-inner {

    color: #f9f6f2;

    background: #edc22e;

    box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.55556), inset 0 0 0 1px rgba(255, 255, 255, 0.33333);

    font-size: 30px;

}

 

@media screen and (max-width: 520px) {

    .tile.tile-2048 .tile-inner {

        font-size: 17px;

    }

}

 

.tile.tile-super .tile-inner {

    color: #f9f6f2;

    background: #3c3a32;

    font-size: 30px;

}

 

@media screen and (max-width: 520px) {

    .tile.tile-super .tile-inner {

        font-size: 17px;

    }

}

 

@-webkit-keyframes appear {

    0% {

        opacity: 0;

        -webkit-transform: scale(0);

        -moz-transform: scale(0);

        transform: scale(0);

    }

 

    100% {

        opacity: 1;

        -webkit-transform: scale(1);

        -moz-transform: scale(1);

        transform: scale(1);

    }

}

 

@-moz-keyframes appear {

    0% {

        opacity: 0;

        -webkit-transform: scale(0);

        -moz-transform: scale(0);

        transform: scale(0);

    }

 

    100% {

        opacity: 1;

        -webkit-transform: scale(1);

        -moz-transform: scale(1);

        transform: scale(1);

    }

}

 

@keyframes appear {

    0% {

        opacity: 0;

        -webkit-transform: scale(0);

        -moz-transform: scale(0);

        transform: scale(0);

    }

 

    100% {

        opacity: 1;

        -webkit-transform: scale(1);

        -moz-transform: scale(1);

        transform: scale(1);

    }

}

 

.tile-new .tile-inner {

    -webkit-animation: appear 200ms ease 100ms;

    -moz-animation: appear 200ms ease 100ms;

    animation: appear 200ms ease 100ms;

    -webkit-animation-fill-mode: backwards;

    -moz-animation-fill-mode: backwards;

    animation-fill-mode: backwards;

}

 

@-webkit-keyframes pop {

    0% {

        -webkit-transform: scale(0);

        -moz-transform: scale(0);

        transform: scale(0);

    }

 

    50% {

        -webkit-transform: scale(1.2);

        -moz-transform: scale(1.2);

        transform: scale(1.2);

    }

 

    100% {

        -webkit-transform: scale(1);

        -moz-transform: scale(1);

        transform: scale(1);

    }

}

 

@-moz-keyframes pop {

    0% {

        -webkit-transform: scale(0);

        -moz-transform: scale(0);

        transform: scale(0);

    }

 

    50% {

        -webkit-transform: scale(1.2);

        -moz-transform: scale(1.2);

        transform: scale(1.2);

    }

 

    100% {

        -webkit-transform: scale(1);

        -moz-transform: scale(1);

        transform: scale(1);

    }

}

 

@keyframes pop {

    0% {

        -webkit-transform: scale(0);

        -moz-transform: scale(0);

        transform: scale(0);

    }

 

    50% {

        -webkit-transform: scale(1.2);

        -moz-transform: scale(1.2);

        transform: scale(1.2);

    }

 

    100% {

        -webkit-transform: scale(1);

        -moz-transform: scale(1);

        transform: scale(1);

    }

}

 

.tile-merged .tile-inner {

    z-index: 20;

    -webkit-animation: pop 200ms ease 100ms;

    -moz-animation: pop 200ms ease 100ms;

    animation: pop 200ms ease 100ms;

    -webkit-animation-fill-mode: backwards;

    -moz-animation-fill-mode: backwards;

    animation-fill-mode: backwards;

}

 

.above-game:after {

    content: "";

    display: block;

    clear: both;

}

 

.game-intro {

    float: left;

    line-height: 42px;

    margin-bottom: 0;

}

 

.restart-button {

    display: inline-block;

    background: #8f7a66;

    border-radius: 3px;

    padding: 0 20px;

    text-decoration: none;

    color: #f9f6f2;

    height: 40px;

    line-height: 42px;

    cursor: pointer;

    display: block;

    text-align: center;

    float: right;

}

 

.game-explanation {

    margin-top: 50px;

}

 

.sharing {

    margin-top: 20px;

    text-align: center;

}

 

.sharing > iframe, .sharing > span, .sharing > form {

    display: inline-block;

    vertical-align: middle;

}

 

@media screen and (max-width: 520px) {

    html, body {

        font-size: 15px;

    }

 

    body {

        margin: 20px 0;

        padding: 0 20px;

    }

 

    h1.title {

        font-size: 27px;

        margin-top: 15px;

    }

 

    .container {

        width: 280px;

        margin: 0 auto;

    }

 

    .score-container, .best-container {

        margin-top: 0;

        padding: 15px 10px;

        min-width: 40px;

    }

 

    .heading {

        margin-bottom: 10px;

    }

 

    .game-intro {

        width: 55%;

        display: block;

        box-sizing: border-box;

        line-height: 1.65;

    }

 

    .restart-button {

        width: 42%;

        padding: 0;

        display: block;

        box-sizing: border-box;

        margin-top: 2px;

    }

 

    .game-container {

        margin-top: 17px;

        position: relative;

        padding: 10px;

        cursor: default;

        -webkit-touch-callout: none;

        -ms-touch-callout: none;

        -webkit-user-select: none;

        -moz-user-select: none;

        -ms-user-select: none;

        -ms-touch-action: none;

        touch-action: none;

        background: #bbada0;

        border-radius: 6px;

        width: 280px;

        height: 280px;

        -webkit-box-sizing: border-box;

        -moz-box-sizing: border-box;

        box-sizing: border-box;

    }

 

    .game-message {

        display: none;

        position: absolute;

        top: 0;

        right: 0;

        bottom: 0;

        left: 0;

        background: rgba(238, 228, 218, 0.73);

        z-index: 100;

        padding-top: 40px;

        text-align: center;

        -webkit-animation: fade-in 800ms ease 1200ms;

        -moz-animation: fade-in 800ms ease 1200ms;

        animation: fade-in 800ms ease 1200ms;

        -webkit-animation-fill-mode: both;

        -moz-animation-fill-mode: both;

        animation-fill-mode: both;

    }

 

    .game-message p {

        font-size: 60px;

        font-weight: bold;

        height: 60px;

        line-height: 60px;

        margin-top: 222px;

    }

 

    .game-message .lower {

        display: block;

        margin-top: 29px;

    }

 

    .game-message .mailing-list {

        margin-top: 52px;

    }

 

    .game-message .mailing-list strong {

        display: block;

        margin-bottom: 10px;

    }

 

    .game-message .mailing-list .mailing-list-email-field {

        width: 230px;

        margin-right: 5px;

    }

 

    .game-message a {

        display: inline-block;

        background: #8f7a66;

        border-radius: 3px;

        padding: 0 20px;

        text-decoration: none;

        color: #f9f6f2;

        height: 40px;

        line-height: 42px;

        cursor: pointer;

        margin-left: 9px;

    }

 

    .game-message a.keep-playing-button {

        display: none;

    }

 

    .game-message .score-sharing {

        display: inline-block;

        vertical-align: middle;

        margin-left: 10px;

    }

 

    .game-message.game-won {

        background: rgba(237, 194, 46, 0.5);

        color: #f9f6f2;

    }

 

    .game-message.game-won a.keep-playing-button {

        display: inline-block;

    }

 

    .game-message.game-won, .game-message.game-over {

        display: block;

    }

 

    .game-message.game-won p, .game-message.game-over p {

        -webkit-animation: slide-up 1.5s ease-in-out 2500ms;

        -moz-animation: slide-up 1.5s ease-in-out 2500ms;

        animation: slide-up 1.5s ease-in-out 2500ms;

        -webkit-animation-fill-mode: both;

        -moz-animation-fill-mode: both;

        animation-fill-mode: both;

    }

 

    .game-message.game-won .mailing-list, .game-message.game-over .mailing-list {

        -webkit-animation: fade-in 1.5s ease-in-out 2500ms;

        -moz-animation: fade-in 1.5s ease-in-out 2500ms;

        animation: fade-in 1.5s ease-in-out 2500ms;

        -webkit-animation-fill-mode: both;

        -moz-animation-fill-mode: both;

        animation-fill-mode: both;

    }

 

    .grid-container {

        position: absolute;

        z-index: 1;

    }

 

    .grid-row {

        margin-bottom: 10px;

    }

 

    .grid-row:last-child {

        margin-bottom: 0;

    }

 

    .grid-row:after {

        content: "";

        display: block;

        clear: both;

    }

 

    .grid-cell {

        width: 57.5px;

        height: 57.5px;

        margin-right: 10px;

        float: left;

        border-radius: 3px;

        background: rgba(238, 228, 218, 0.35);

    }

 

    .grid-cell:last-child {

        margin-right: 0;

    }

 

    .tile-container {

        position: absolute;

        z-index: 2;

    }

 

    .tile, .tile .tile-inner {

        width: 58px;

        height: 58px;

        line-height: 67.5px;

    }

 

    .tile.tile-position-1-1 {

        -webkit-transform: translate(0px, 0px);

        -moz-transform: translate(0px, 0px);

        transform: translate(0px, 0px);

    }

 

    .tile.tile-position-1-2 {

        -webkit-transform: translate(0px, 67px);

        -moz-transform: translate(0px, 67px);

        transform: translate(0px, 67px);

    }

 

    .tile.tile-position-1-3 {

        -webkit-transform: translate(0px, 135px);

        -moz-transform: translate(0px, 135px);

        transform: translate(0px, 135px);

    }

 

    .tile.tile-position-1-4 {

        -webkit-transform: translate(0px, 202px);

        -moz-transform: translate(0px, 202px);

        transform: translate(0px, 202px);

    }

 

    .tile.tile-position-2-1 {

        -webkit-transform: translate(67px, 0px);

        -moz-transform: translate(67px, 0px);

        transform: translate(67px, 0px);

    }

 

    .tile.tile-position-2-2 {

        -webkit-transform: translate(67px, 67px);

        -moz-transform: translate(67px, 67px);

        transform: translate(67px, 67px);

    }

 

    .tile.tile-position-2-3 {

        -webkit-transform: translate(67px, 135px);

        -moz-transform: translate(67px, 135px);

        transform: translate(67px, 135px);

    }

 

    .tile.tile-position-2-4 {

        -webkit-transform: translate(67px, 202px);

        -moz-transform: translate(67px, 202px);

        transform: translate(67px, 202px);

    }

 

    .tile.tile-position-3-1 {

        -webkit-transform: translate(135px, 0px);

        -moz-transform: translate(135px, 0px);

        transform: translate(135px, 0px);

    }

 

    .tile.tile-position-3-2 {

        -webkit-transform: translate(135px, 67px);

        -moz-transform: translate(135px, 67px);

        transform: translate(135px, 67px);

    }

 

    .tile.tile-position-3-3 {

        -webkit-transform: translate(135px, 135px);

        -moz-transform: translate(135px, 135px);

        transform: translate(135px, 135px);

    }

 

    .tile.tile-position-3-4 {

        -webkit-transform: translate(135px, 202px);

        -moz-transform: translate(135px, 202px);

        transform: translate(135px, 202px);

    }

 

    .tile.tile-position-4-1 {

        -webkit-transform: translate(202px, 0px);

        -moz-transform: translate(202px, 0px);

        transform: translate(202px, 0px);

    }

 

    .tile.tile-position-4-2 {

        -webkit-transform: translate(202px, 67px);

        -moz-transform: translate(202px, 67px);

        transform: translate(202px, 67px);

    }

 

    .tile.tile-position-4-3 {

        -webkit-transform: translate(202px, 135px);

        -moz-transform: translate(202px, 135px);

        transform: translate(202px, 135px);

    }

 

    .tile.tile-position-4-4 {

        -webkit-transform: translate(202px, 202px);

        -moz-transform: translate(202px, 202px);

        transform: translate(202px, 202px);

    }

 

    .tile .tile-inner {

        font-size: 25px;

    }

 

    .game-message {

        padding-top: 0;

    }

 

    .game-message p {

        font-size: 30px !important;

        height: 30px !important;

        line-height: 30px !important;

        margin-top: 32% !important;

        margin-bottom: 0 !important;

    }

 

    .game-message .lower {

        margin-top: 10px !important;

    }

 

    .game-message.game-won .score-sharing {

        margin-top: 10px;

    }

 

    .game-message.game-over .mailing-list {

        margin-top: 25px;

    }

 

    .game-message .mailing-list {

        margin-top: 10px;

    }

 

    .game-message .mailing-list .mailing-list-email-field {

        width: 180px;

    }

 

    .sharing > iframe, .sharing > span, .sharing > form {

        display: block;

        margin: 0 auto;

        margin-bottom: 20px;

    }

}

 

.pp-donate button {

    -webkit-appearance: none;

    -moz-appearance: none;

    appearance: none;

    border: none;

    font: inherit;

    color: inherit;

    display: inline-block;

    background: #8f7a66;

    border-radius: 3px;

    padding: 0 20px;

    text-decoration: none;

    color: #f9f6f2;

    height: 40px;

    line-height: 42px;

    cursor: pointer;

}

 

.pp-donate button img {

    vertical-align: -4px;

    margin-right: 8px;

}

 

.btc-donate {

    position: relative;

    margin-left: 10px;

    display: inline-block;

    background: #8f7a66;

    border-radius: 3px;

    padding: 0 20px;

    text-decoration: none;

    color: #f9f6f2;

    height: 40px;

    line-height: 42px;

    cursor: pointer;

}

 

.btc-donate img {

    vertical-align: -4px;

    margin-right: 8px;

}

 

.btc-donate a {

    color: #f9f6f2;

    text-decoration: none;

    font-weight: normal;

}

 

.btc-donate .address {

    cursor: auto;

    position: absolute;

    width: 340px;

    right: 50%;

    margin-right: -170px;

    padding-bottom: 7px;

    top: -30px;

    opacity: 0;

    pointer-events: none;

    -webkit-transition: 400ms ease;

    -moz-transition: 400ms ease;

    transition: 400ms ease;

    -webkit-transition-property: top, opacity;

    -moz-transition-property: top, opacity;

    transition-property: top, opacity;

}

 

.btc-donate .address:after {

    position: absolute;

    border-top: 10px solid #bbada0;

    border-right: 7px solid transparent;

    border-left: 7px solid transparent;

    content: "";

    bottom: 0px;

    left: 50%;

    margin-left: -7px;

}

 

.btc-donate .address code {

    background-color: #bbada0;

    padding: 10px 15px;

    width: 100%;

    border-radius: 3px;

    line-height: 1;

    font-weight: normal;

    font-size: 15px;

    font-family: Consolas, "Liberation Mono", Courier, monospace;

    text-align: center;

}

 

.btc-donate:hover .address, .btc-donate .address:hover .address {

    opacity: 1;

    top: -45px;

    pointer-events: auto;

}

 

@media screen and (max-width: 480px) {

    .btc-donate {

        width: 120px;

    }

 

    .btc-donate .address {

        margin-right: -150px;

        width: 300px;

    }

 

    .btc-donate .address code {

        font-size: 13px;

    }

 

    .btc-donate .address:after {

        left: 50%;

        bottom: 2px;

    }

}

      </style>

</head>

<body>

<div class="container">

    <div class="heading">

        <h1 class="title">2048小游戏</h1>

        <div class="scores-container">

            <div class="score-container">

                <div class="score-addition"></div>

            </div>

            <div class="best-container"></div>

        </div>

    </div>

    <div class="above-game">

        <a class="restart-button">新游戏</a>

    </div>

    <div class="game-container">

        <div class="game-message">

            <p>

            </p>

            <div class="lower">

                <a class="keep-playing-button">继续前进</a>

                <a class="retry-button">再试一次</a>

                <div class="score-sharing"></div>

            </div>

        </div>

        <div class="grid-container">

            <div class="grid-row">

                <div class="grid-cell">

                </div>

                <div class="grid-cell">

                </div>

                <div class="grid-cell">

                </div>

                <div class="grid-cell">

                </div>

            </div>

            <div class="grid-row">

                <div class="grid-cell">

                </div>

                <div class="grid-cell">

                </div>

                <div class="grid-cell">

                </div>

                <div class="grid-cell">

                </div>

            </div>

            <div class="grid-row">

                <div class="grid-cell">

                </div>

                <div class="grid-cell">

                </div>

                <div class="grid-cell">

                </div>

                <div class="grid-cell">

                </div>

            </div>

            <div class="grid-row">

                <div class="grid-cell">

                </div>

                <div class="grid-cell">

                </div>

                <div class="grid-cell">

                </div>

                <div class="grid-cell">

                </div>

            </div>

        </div>

        <div class="tile-container"></div>

    </div>

    

    <script>

     Function.prototype.bind = Function.prototype.bind || function (target) {

  var self = this;

  return function (args) {

    if (!(args instanceof Array)) {

      args = [args];

    }

    self.apply(target, args);

  };

};

(function () {

  if (typeof window.Element === "undefined" ||

      "classList" in document.documentElement) {

    return;

  }

  var prototype = Array.prototype,

      push = prototype.push,

      splice = prototype.splice,

      join = prototype.join;

  function DOMTokenList(el) {

    this.el = el;

    // The className needs to be trimmed and split on whitespace

    // to retrieve a list of classes.

    var classes = el.className.replace(/^\s+|\s+$/g, '').split(/\s+/);

    for (var i = 0; i < classes.length; i++) {

      push.call(this, classes[i]);

    }

  }

  DOMTokenList.prototype = {

    add: function (token) {

      if (this.contains(token)) return;

      push.call(this, token);

      this.el.className = this.toString();

    },

    contains: function (token) {

      return this.el.className.indexOf(token) != -1;

    },

    item: function (index) {

      return this[index] || null;

    },

    remove: function (token) {

      if (!this.contains(token)) return;

      for (var i = 0; i < this.length; i++) {

        if (this[i] == token) break;

      }

      splice.call(this, i, 1);

      this.el.className = this.toString();

    },

    toString: function () {

      return join.call(this, ' ');

    },

    toggle: function (token) {

      if (!this.contains(token)) {

        this.add(token);

      } else {

        this.remove(token);

      }

      return this.contains(token);

    }

  };

  window.DOMTokenList = DOMTokenList;

  function defineElementGetter(obj, prop, getter) {

    if (Object.defineProperty) {

      Object.defineProperty(obj, prop, {

        get: getter

      });

    } else {

      obj.__defineGetter__(prop, getter);

    }

  }

  defineElementGetter(HTMLElement.prototype, 'classList', function () {

    return new DOMTokenList(this);

  });

})();

(function() {

  var lastTime = 0;

  var vendors = ['webkit', 'moz'];

  for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {

    window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];

    window.cancelAnimationFrame =

    window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];

  }

  if (!window.requestAnimationFrame) {

    window.requestAnimationFrame = function(callback, element) {

      var currTime = new Date().getTime();

      var timeToCall = Math.max(0, 16 - (currTime - lastTime));

      var id = window.setTimeout(function() { callback(currTime + timeToCall); },

      timeToCall);

      lastTime = currTime + timeToCall;

      return id;

    };

  }

  if (!window.cancelAnimationFrame) {

    window.cancelAnimationFrame = function(id) {

      clearTimeout(id);

    };

  }

}());

function KeyboardInputManager() {

  this.events = {};

  if (window.navigator.msPointerEnabled) {

    //Internet Explorer 10 style

    this.eventTouchstart    = "MSPointerDown";

    this.eventTouchmove     = "MSPointerMove";

    this.eventTouchend      = "MSPointerUp";

  } else {

    this.eventTouchstart    = "touchstart";

    this.eventTouchmove     = "touchmove";

    this.eventTouchend      = "touchend";

  }

  this.listen();

}

KeyboardInputManager.prototype.on = function (event, callback) {

  if (!this.events[event]) {

    this.events[event] = [];

  }

  this.events[event].push(callback);

};

KeyboardInputManager.prototype.emit = function (event, data) {

  var callbacks = this.events[event];

  if (callbacks) {

    callbacks.forEach(function (callback) {

      callback(data);

    });

  }

};

KeyboardInputManager.prototype.listen = function () {

  var self = this;

  var map = {

    38: 0, // Up

    39: 1, // Right

    40: 2, // Down

    37: 3, // Left

    75: 0, // Vim up

    76: 1, // Vim right

    74: 2, // Vim down

    72: 3, // Vim left

    87: 0, // W

    68: 1, // D

    83: 2, // S

    65: 3  // A

  };

  // Respond to direction keys

  document.addEventListener("keydown", function (event) {

    var modifiers = event.altKey || event.ctrlKey || event.metaKey ||

                    event.shiftKey;

    var mapped    = map[event.which];

    // Ignore the event if it's happening in a text field

    if (self.targetIsInput(event)) return;

    if (!modifiers) {

      if (mapped !== undefined) {

        event.preventDefault();

        self.emit("move", mapped);

      }

    }

    // R key restarts the game

    if (!modifiers && event.which === 82) {

      self.restart.call(self, event);

    }

  });

  // Respond to button presses

  this.bindButtonPress(".retry-button", this.restart);

  this.bindButtonPress(".restart-button", this.restart);

  this.bindButtonPress(".keep-playing-button", this.keepPlaying);

  // Respond to swipe events

  var touchStartClientX, touchStartClientY;

  var gameContainer = document.getElementsByClassName("game-container")[0];

  gameContainer.addEventListener(this.eventTouchstart, function (event) {

    if ((!window.navigator.msPointerEnabled && event.touches.length > 1) ||

        event.targetTouches > 1 ||

        self.targetIsInput(event)) {

      return; // Ignore if touching with more than 1 finger or touching input

    }

    if (window.navigator.msPointerEnabled) {

      touchStartClientX = event.pageX;

      touchStartClientY = event.pageY;

    } else {

      touchStartClientX = event.touches[0].clientX;

      touchStartClientY = event.touches[0].clientY;

    }

    event.preventDefault();

  });

  gameContainer.addEventListener(this.eventTouchmove, function (event) {

    event.preventDefault();

  });

  gameContainer.addEventListener(this.eventTouchend, function (event) {

    if ((!window.navigator.msPointerEnabled && event.touches.length > 0) ||

        event.targetTouches > 0 ||

        self.targetIsInput(event)) {

      return; // Ignore if still touching with one or more fingers or input

    }

    var touchEndClientX, touchEndClientY;

    if (window.navigator.msPointerEnabled) {

      touchEndClientX = event.pageX;

      touchEndClientY = event.pageY;

    } else {

      touchEndClientX = event.changedTouches[0].clientX;

      touchEndClientY = event.changedTouches[0].clientY;

    }

    var dx = touchEndClientX - touchStartClientX;

    var absDx = Math.abs(dx);

    var dy = touchEndClientY - touchStartClientY;

    var absDy = Math.abs(dy);

    if (Math.max(absDx, absDy) > 10) {

      // (right : left) : (down : up)

      self.emit("move", absDx > absDy ? (dx > 0 ? 1 : 3) : (dy > 0 ? 2 : 0));

    }

  });

};

KeyboardInputManager.prototype.restart = function (event) {

  event.preventDefault();

  this.emit("restart");

};

KeyboardInputManager.prototype.keepPlaying = function (event) {

  event.preventDefault();

  this.emit("keepPlaying");

};

KeyboardInputManager.prototype.bindButtonPress = function (selector, fn) {

  var button = document.querySelector(selector);

  button.addEventListener("click", fn.bind(this));

  button.addEventListener(this.eventTouchend, fn.bind(this));

};

KeyboardInputManager.prototype.targetIsInput = function (event) {

  return event.target.tagName.toLowerCase() === "input";

};

function HTMLActuator() {

  this.tileContainer    = document.querySelector(".tile-container");

  this.scoreContainer   = document.querySelector(".score-container");

  this.bestContainer    = document.querySelector(".best-container");

  this.messageContainer = document.querySelector(".game-message");

  this.sharingContainer = document.querySelector(".score-sharing");

  this.score = 0;

}

HTMLActuator.prototype.actuate = function (grid, metadata) {

  var self = this;

  window.requestAnimationFrame(function () {

    self.clearContainer(self.tileContainer);

    grid.cells.forEach(function (column) {

      column.forEach(function (cell) {

        if (cell) {

          self.addTile(cell);

        }

      });

    });

    self.updateScore(metadata.score);

    self.updateBestScore(metadata.bestScore);

    if (metadata.terminated) {

      if (metadata.over) {

        self.message(false); // You lose

      } else if (metadata.won) {

        self.message(true); // You win!

      }

    }

  });

};

// Continues the game (both restart and keep playing)

HTMLActuator.prototype.continueGame = function () {

  if (typeof ga !== "undefined") {

    ga("send", "event", "game", "restart");

  }

  this.clearMessage();

};

HTMLActuator.prototype.clearContainer = function (container) {

  while (container.firstChild) {

    container.removeChild(container.firstChild);

  }

};

//HTMLActuator.prototype.tileHTML = ["菜鸟", "入门", "码畜", "码奴", "码农", "IT民工", "IT工程师", "IT人才", "IT精英", "IT大哥", "IT领袖"];

//HTMLActuator.prototype.tileHTML = ["2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048"];

//HTMLActuator.prototype.tileHTML = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "win"];

HTMLActuator.prototype.tileHTML = ["2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048"];

HTMLActuator.prototype.addTile = function (tile) {

  var self = this;

  var wrapper   = document.createElement("div");

  var inner     = document.createElement("div");

  var position  = tile.previousPosition || { x: tile.x, y: tile.y };

  var positionClass = this.positionClass(position);

  // We can't use classlist because it somehow glitches when replacing classes

  var classes = ["tile", "tile-" + tile.value, positionClass];

  if (tile.value > 2048) classes.push("tile-super");

  this.applyClasses(wrapper, classes);

  inner.classList.add("tile-inner");

  inner.textContent = HTMLActuator.prototype.tileHTML[Math.log(tile.value) / Math.LN2 - 1] || tile.value;

  if (tile.previousPosition) {

    // Make sure that the tile gets rendered in the previous position first

    window.requestAnimationFrame(function () {

      classes[2] = self.positionClass({ x: tile.x, y: tile.y });

      self.applyClasses(wrapper, classes); // Update the position

    });

  } else if (tile.mergedFrom) {

    classes.push("tile-merged");

    this.applyClasses(wrapper, classes);

    // Render the tiles that merged

    tile.mergedFrom.forEach(function (merged) {

      self.addTile(merged);

    });

  } else {

    classes.push("tile-new");

    this.applyClasses(wrapper, classes);

  }

  // Add the inner part of the tile to the wrapper

  wrapper.appendChild(inner);

  // Put the tile on the board

  this.tileContainer.appendChild(wrapper);

};

HTMLActuator.prototype.applyClasses = function (element, classes) {

  element.setAttribute("class", classes.join(" "));

};

HTMLActuator.prototype.normalizePosition = function (position) {

  return { x: position.x + 1, y: position.y + 1 };

};

HTMLActuator.prototype.positionClass = function (position) {

  position = this.normalizePosition(position);

  return "tile-position-" + position.x + "-" + position.y;

};

HTMLActuator.prototype.updateScore = function (score) {

  this.clearContainer(this.scoreContainer);

  var difference = score - this.score;

  this.score = score;

  this.scoreContainer.textContent = this.score;

  if (difference > 0) {

    var addition = document.createElement("div");

    addition.classList.add("score-addition");

    addition.textContent = "+" + difference;

    this.scoreContainer.appendChild(addition);

  }

};

HTMLActuator.prototype.updateBestScore = function (bestScore) {

  this.bestContainer.textContent = bestScore;

};

HTMLActuator.prototype.message = function (won) {

  var type    = won ? "game-won" : "game-over";

  var message = won ? "You win!" : "Game over!";

  if (typeof ga !== "undefined") {

    ga("send", "event", "game", "end", type, this.score);

  }

  this.messageContainer.classList.add(type);

  this.messageContainer.getElementsByTagName("p")[0].textContent = message;

  this.clearContainer(this.sharingContainer);

  this.sharingContainer.appendChild(this.scoreTweetButton());

  //twttr.widgets.load();

};

HTMLActuator.prototype.clearMessage = function () {

  // IE only takes one value to remove at a time.

  this.messageContainer.classList.remove("game-won");

  this.messageContainer.classList.remove("game-over");

};

HTMLActuator.prototype.scoreTweetButton = function () {

  var tweet = document.createElement("a");

  tweet.classList.add("twitter-share-button");

  tweet.setAttribute("href", "https://twitter.com/share");

  tweet.setAttribute("data-via", "gabrielecirulli");

  tweet.setAttribute("data-url", "http://git.io/2048");

  tweet.setAttribute("data-counturl", "http://gabrielecirulli.github.io/2048/");

  tweet.textContent = "Tweet";

  var text = "I scored " + this.score + " points at 2048, a game where you " +

             "join numbers to score high! #2048game";

  tweet.setAttribute("data-text", text);

  return tweet;

};

function Grid(size, previousState) {

  this.size = size;

  this.cells = previousState ? this.fromState(previousState) : this.empty();

}

// Build a grid of the specified size

Grid.prototype.empty = function () {

  var cells = [];

  for (var x = 0; x < this.size; x++) {

    var row = cells[x] = [];

    for (var y = 0; y < this.size; y++) {

      row.push(null);

    }

  }

  return cells;

};

Grid.prototype.fromState = function (state) {

  var cells = [];

  for (var x = 0; x < this.size; x++) {

    var row = cells[x] = [];

    for (var y = 0; y < this.size; y++) {

      var tile = state[x][y];

      row.push(tile ? new Tile(tile.position, tile.value) : null);

    }

  }

  return cells;

};

// Find the first available random position

Grid.prototype.randomAvailableCell = function () {

  var cells = this.availableCells();

  if (cells.length) {

    return cells[Math.floor(Math.random() * cells.length)];

  }

};

Grid.prototype.availableCells = function () {

  var cells = [];

  this.eachCell(function (x, y, tile) {

    if (!tile) {

      cells.push({ x: x, y: y });

    }

  });

  return cells;

};

// Call callback for every cell

Grid.prototype.eachCell = function (callback) {

  for (var x = 0; x < this.size; x++) {

    for (var y = 0; y < this.size; y++) {

      callback(x, y, this.cells[x][y]);

    }

  }

};

// Check if there are any cells available

Grid.prototype.cellsAvailable = function () {

  return !!this.availableCells().length;

};

// Check if the specified cell is taken

Grid.prototype.cellAvailable = function (cell) {

  return !this.cellOccupied(cell);

};

Grid.prototype.cellOccupied = function (cell) {

  return !!this.cellContent(cell);

};

Grid.prototype.cellContent = function (cell) {

  if (this.withinBounds(cell)) {

    return this.cells[cell.x][cell.y];

  } else {

    return null;

  }

};

// Inserts a tile at its position

Grid.prototype.insertTile = function (tile) {

  this.cells[tile.x][tile.y] = tile;

};

Grid.prototype.removeTile = function (tile) {

  this.cells[tile.x][tile.y] = null;

};

Grid.prototype.withinBounds = function (position) {

  return position.x >= 0 && position.x < this.size &&

         position.y >= 0 && position.y < this.size;

};

Grid.prototype.serialize = function () {

  var cellState = [];

  for (var x = 0; x < this.size; x++) {

    var row = cellState[x] = [];

    for (var y = 0; y < this.size; y++) {

      row.push(this.cells[x][y] ? this.cells[x][y].serialize() : null);

    }

  }

  return {

    size: this.size,

    cells: cellState

  };

};

function Tile(position, value) {

  this.x                = position.x;

  this.y                = position.y;

  this.value            = value || 2;

  this.previousPosition = null;

  this.mergedFrom       = null; // Tracks tiles that merged together

}

Tile.prototype.savePosition = function () {

  this.previousPosition = { x: this.x, y: this.y };

};

Tile.prototype.updatePosition = function (position) {

  this.x = position.x;

  this.y = position.y;

};

Tile.prototype.serialize = function () {

  return {

    position: {

      x: this.x,

      y: this.y

    },

    value: this.value

  };

};

window.fakeStorage = {

  _data: {},

  setItem: function (id, val) {

    return this._data[id] = String(val);

  },

  getItem: function (id) {

    return this._data.hasOwnProperty(id) ? this._data[id] : undefined;

  },

  removeItem: function (id) {

    return delete this._data[id];

  },

  clear: function () {

    return this._data = {};

  }

};

function LocalStorageManager() {

  this.bestScoreKey     = "bestScore";

  this.gameStateKey     = "gameState";

  var supported = this.localStorageSupported();

  this.storage = supported ? window.localStorage : window.fakeStorage;

}

LocalStorageManager.prototype.localStorageSupported = function () {

  var testKey = "test";

  var storage = window.localStorage;

  try {

    storage.setItem(testKey, "1");

    storage.removeItem(testKey);

    return true;

  } catch (error) {

    return false;

  }

};

// Best score getters/setters

LocalStorageManager.prototype.getBestScore = function () {

  return this.storage.getItem(this.bestScoreKey) || 0;

};

LocalStorageManager.prototype.setBestScore = function (score) {

  this.storage.setItem(this.bestScoreKey, score);

};

// Game state getters/setters and clearing

LocalStorageManager.prototype.getGameState = function () {

  var stateJSON = this.storage.getItem(this.gameStateKey);

  return stateJSON ? JSON.parse(stateJSON) : null;

};

LocalStorageManager.prototype.setGameState = function (gameState) {

  this.storage.setItem(this.gameStateKey, JSON.stringify(gameState));

};

LocalStorageManager.prototype.clearGameState = function () {

  this.storage.removeItem(this.gameStateKey);

};

function GameManager(size, InputManager, Actuator, StorageManager) {

  this.size           = size; // Size of the grid

  this.inputManager   = new InputManager;

  this.storageManager = new StorageManager;

  this.actuator       = new Actuator;

  this.startTiles     = 2;

  this.inputManager.on("move", this.move.bind(this));

  this.inputManager.on("restart", this.restart.bind(this));

  this.inputManager.on("keepPlaying", this.keepPlaying.bind(this));

  this.setup();

}

// Restart the game

GameManager.prototype.restart = function () {

  this.storageManager.clearGameState();

  this.actuator.continueGame(); // Clear the game won/lost message

  this.setup();

};

// Keep playing after winning (allows going over 2048)

GameManager.prototype.keepPlaying = function () {

  this.keepPlaying = true;

  this.actuator.continueGame(); // Clear the game won/lost message

};

// Return true if the game is lost, or has won and the user hasn't kept playing

GameManager.prototype.isGameTerminated = function () {

  if (this.over || (this.won && !this.keepPlaying)) {

    return true;

  } else {

    return false;

  }

};

// Set up the game

GameManager.prototype.setup = function () {

  var previousState = this.storageManager.getGameState();

  // Reload the game from a previous game if present

  if (previousState) {

    this.grid        = new Grid(previousState.grid.size,

                                previousState.grid.cells); // Reload grid

    this.score       = previousState.score;

    this.over        = previousState.over;

    this.won         = previousState.won;

    this.keepPlaying = previousState.keepPlaying;

  } else {

    this.grid        = new Grid(this.size);

    this.score       = 0;

    this.over        = false;

    this.won         = false;

    this.keepPlaying = false;

    // Add the initial tiles

    this.addStartTiles();

  }

  // Update the actuator

  this.actuate();

};

// Set up the initial tiles to start the game with

GameManager.prototype.addStartTiles = function () {

  for (var i = 0; i < this.startTiles; i++) {

    this.addRandomTile();

  }

};

// Adds a tile in a random position

GameManager.prototype.addRandomTile = function () {

  if (this.grid.cellsAvailable()) {

    var value = Math.random() < 0.9 ? 2 : 4;

    var tile = new Tile(this.grid.randomAvailableCell(), value);

    this.grid.insertTile(tile);

  }

};

// Sends the updated grid to the actuator

GameManager.prototype.actuate = function () {

  if (this.storageManager.getBestScore() < this.score) {

    this.storageManager.setBestScore(this.score);

  }

  // Clear the state when the game is over (game over only, not win)

  if (this.over) {

    this.storageManager.clearGameState();

  } else {

    this.storageManager.setGameState(this.serialize());

  }

  this.actuator.actuate(this.grid, {

    score:      this.score,

    over:       this.over,

    won:        this.won,

    bestScore:  this.storageManager.getBestScore(),

    terminated: this.isGameTerminated()

  });

};

// Represent the current game as an object

GameManager.prototype.serialize = function () {

  return {

    grid:        this.grid.serialize(),

    score:       this.score,

    over:        this.over,

    won:         this.won,

    keepPlaying: this.keepPlaying

  };

};

// Save all tile positions and remove merger info

GameManager.prototype.prepareTiles = function () {

  this.grid.eachCell(function (x, y, tile) {

    if (tile) {

      tile.mergedFrom = null;

      tile.savePosition();

    }

  });

};

// Move a tile and its representation

GameManager.prototype.moveTile = function (tile, cell) {

  this.grid.cells[tile.x][tile.y] = null;

  this.grid.cells[cell.x][cell.y] = tile;

  tile.updatePosition(cell);

};

// Move tiles on the grid in the specified direction

GameManager.prototype.move = function (direction) {

  // 0: up, 1: right, 2: down, 3: left

  var self = this;

  if (this.isGameTerminated()) return; // Don't do anything if the game's over

  var cell, tile;

  var vector     = this.getVector(direction);

  var traversals = this.buildTraversals(vector);

  var moved      = false;

  // Save the current tile positions and remove merger information

  this.prepareTiles();

  // Traverse the grid in the right direction and move tiles

  traversals.x.forEach(function (x) {

    traversals.y.forEach(function (y) {

      cell = { x: x, y: y };

      tile = self.grid.cellContent(cell);

      if (tile) {

        var positions = self.findFarthestPosition(cell, vector);

        var next      = self.grid.cellContent(positions.next);

        // Only one merger per row traversal?

        if (next && next.value === tile.value && !next.mergedFrom) {

          var merged = new Tile(positions.next, tile.value * 2);

          merged.mergedFrom = [tile, next];

          self.grid.insertTile(merged);

          self.grid.removeTile(tile);

          // Converge the two tiles' positions

          tile.updatePosition(positions.next);

          // Update the score

          self.score += merged.value;

          // The mighty 2048 tile

          if (merged.value === 2048) self.won = true;

        } else {

          self.moveTile(tile, positions.farthest);

        }

        if (!self.positionsEqual(cell, tile)) {

          moved = true; // The tile moved from its original cell!

        }

      }

    });

  });

  if (moved) {

    this.addRandomTile();

    if (!this.movesAvailable()) {

      this.over = true; // Game over!

    }

    this.actuate();

  }

};

// Get the vector representing the chosen direction

GameManager.prototype.getVector = function (direction) {

  // Vectors representing tile movement

  var map = {

    0: { x: 0,  y: -1 }, // Up

    1: { x: 1,  y: 0 },  // Right

    2: { x: 0,  y: 1 },  // Down

    3: { x: -1, y: 0 }   // Left

  };

  return map[direction];

};

// Build a list of positions to traverse in the right order

GameManager.prototype.buildTraversals = function (vector) {

  var traversals = { x: [], y: [] };

  for (var pos = 0; pos < this.size; pos++) {

    traversals.x.push(pos);

    traversals.y.push(pos);

  }

  // Always traverse from the farthest cell in the chosen direction

  if (vector.x === 1) traversals.x = traversals.x.reverse();

  if (vector.y === 1) traversals.y = traversals.y.reverse();

  return traversals;

};

GameManager.prototype.findFarthestPosition = function (cell, vector) {

  var previous;

  // Progress towards the vector direction until an obstacle is found

  do {

    previous = cell;

    cell     = { x: previous.x + vector.x, y: previous.y + vector.y };

  } while (this.grid.withinBounds(cell) &&

           this.grid.cellAvailable(cell));

  return {

    farthest: previous,

    next: cell // Used to check if a merge is required

  };

};

GameManager.prototype.movesAvailable = function () {

  return this.grid.cellsAvailable() || this.tileMatchesAvailable();

};

// Check for available matches between tiles (more expensive check)

GameManager.prototype.tileMatchesAvailable = function () {

  var self = this;

  var tile;

  for (var x = 0; x < this.size; x++) {

    for (var y = 0; y < this.size; y++) {

      tile = this.grid.cellContent({ x: x, y: y });

      if (tile) {

        for (var direction = 0; direction < 4; direction++) {

          var vector = self.getVector(direction);

          var cell   = { x: x + vector.x, y: y + vector.y };

          var other  = self.grid.cellContent(cell);

          if (other && other.value === tile.value) {

            return true; // These two tiles can be merged

          }

        }

      }

    }

  }

  return false;

};

GameManager.prototype.positionsEqual = function (first, second) {

  return first.x === second.x && first.y === second.y;

};

// Wait till the browser is ready to render the game (avoids glitches)

window.requestAnimationFrame(function () {

  new GameManager(4, KeyboardInputManager, HTMLActuator, LocalStorageManager);

}); 

    </script>

    <div style="text-align:center;">

<p><a href="" title="" target="_blank"></a></p>

</div>

</div></body>

</html>

 

历史上的今天
01月
11
    抱歉,历史上的今天作者很懒,什么都没写!
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。