zoukankan      html  css  js  c++  java
  • 兵部-2048

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta charset="utf-8">
    <title>2048</title>
    <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0, maximum-scale=1, user-scalable=no, minimal-ui">
    </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">New Game</a>
    </div>
    <div class="game-container">
    <div class="game-message">
    <p>
    </p>
    <div class="lower">
    <a class="keep-playing-button">Keep going</a>
    <a class="retry-button">Try again</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 src="./js/bind_polyfill.js"></script>
    <script src="./js/classlist_polyfill.js"></script>
    <script src="./js/animframe_polyfill.js"></script>
    <script src="./js/keyboard_input_manager.js"></script>
    <script src="./js/html_actuator.js"></script>
    <script src="./js/grid.js"></script>
    <script src="./js/tile.js"></script>
    <script src="./js/local_storage_manager.js"></script>
    <script src="./js/game_manager.js"></script>
    <script src="./js/application.js"></script>
    </body>
    </html>

    <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;
    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 {
    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;
    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 {
    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 {
    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 {
    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- 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- 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- 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- 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- 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- 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- 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- 520px) {
    html, body {
    font-size: 15px;
    }

    body {
    margin: 20px 0;
    padding: 0 20px;
    }

    h1.title {
    font-size: 27px;
    margin-top: 15px;
    }

    .container {
    280px;
    margin: 0 auto;
    }

    .score-container, .best-container {
    margin-top: 0;
    padding: 15px 10px;
    min- 40px;
    }

    .heading {
    margin-bottom: 10px;
    }

    .game-intro {
    55%;
    display: block;
    box-sizing: border-box;
    line-height: 1.65;
    }

    .restart-button {
    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;
    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 {
    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 {
    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 {
    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 {
    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;
    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;
    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- 480px) {
    .btc-donate {
    120px;
    }

    .btc-donate .address {
    margin-right: -150px;
    300px;
    }

    .btc-donate .address code {
    font-size: 13px;
    }

    .btc-donate .address:after {
    left: 50%;
    bottom: 2px;
    }
    }
    </style>

    <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 = ["工兵", "班长", "排长", "连长", "营长", "团长", "旅长", "师长", "军长", "司令", "军旗"];

    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>

    <!-- Generated by RunJS (Wed Apr 23 15:22:33 CST 2014) 2ms -->

  • 相关阅读:
    JAVA Unsafe类
    进程通信的五种普通方法
    监控API的实现 周末补
    INLINE HOOK 简单实现
    跨域资源请求方式
    在博客园放入“可运行”javascript代码
    一些学习资源
    XXE篇-本着就了解安全本质的想法,尽可能的用通俗易懂的语言去解释安全漏洞问题
    Mongodb注入
    SSRF篇-本着就了解安全本质的想法,尽可能的用通俗易懂的语言去解释安全漏洞问题
  • 原文地址:https://www.cnblogs.com/zyr997/p/other.html
Copyright © 2011-2022 走看看