├── 2048.gif ├── README.md ├── LICENSE ├── index.html ├── 2048.css ├── normalize.css ├── 2048.js └── 2048lib.js /2048.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/banana255/2048/HEAD/2048.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 2048 2 | 3 | > 本页面目前仅适合在移动端使用 4 | > 兼容性主要考虑 chrome 内核 和 safari 内核 的浏览器 5 | 6 | demo: http://www.bananaSS.cc/2048/ 7 | 8 | ![效果图](2048.gif) 9 | 10 | 17/2/2 11 | 12 | - 运用面向过程完成 2048 的逻辑部分 (./2048lib.js) 13 | 14 | 17/2/3 15 | 16 | - 引入 Normalize.css, 提供了跨浏览器的高度一致性 17 | 18 | - 实现界面 & 移动触发 & 阻塞页面滚动 19 | 20 | - 添加 新增元素 动画 21 | 22 | 17/2/4 23 | - 无移动时,不添加新元素 24 | 25 | - 添加记分数功能 26 | 27 | - 界面上的调整 28 | 29 | - 逻辑添加了判断 成功 和 失败 30 | 31 | - 界面上添加了 成功 和 失败 的动画 32 | 33 | 17/2/5 34 | 35 | - 采用面向对象方法,改写 2048lib.js 36 | 37 | - 添加游戏数据本地存储功能 38 | 39 | 17/2/7 40 | 41 | - 添加悔棋功能 42 | 43 | - 改进算法,80% 概率生成 2, 20% 概率生成 4 44 | 45 | - 添加合并 css3 动画 46 | 47 | - 保存动画效果 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 大香蕉 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 大香蕉的 2048 6 | 7 | 8 | 9 | 10 | 11 | 17 | 18 | 19 |
20 |
21 | 22 | 2048 23 | 4 X 4 24 | 25 |
26 | 27 | 28 | 得分 29 | 0 30 | 31 | 32 | 最高分 33 | 0 34 | 35 | 36 | 37 | 悔棋 38 | 新游戏 39 | 40 |
41 |
42 |
43 | 44 | 45 | 46 | 49 | 52 | 55 | 58 | 59 | 60 | 63 | 66 | 69 | 72 | 73 | 74 | 77 | 80 | 83 | 86 | 87 | 88 | 91 | 94 | 97 | 100 | 101 |
47 | 48 | 50 | 51 | 53 | 54 | 56 | 57 |
61 | 62 | 64 | 65 | 67 | 68 | 70 | 71 |
75 | 76 | 78 | 79 | 81 | 82 | 84 | 85 |
89 | 90 | 92 | 93 | 95 | 96 | 98 | 99 |
102 |
103 | 115 |
116 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /2048.css: -------------------------------------------------------------------------------- 1 | .t2048-game { 2 | height: 100vh; 3 | background: #fbf8ee; 4 | } 5 | 6 | * { 7 | box-sizing: border-box; 8 | user-select: none; 9 | -webkit-user-select: none; 10 | } 11 | 12 | /* header */ 13 | .t2048-header { 14 | height: 25%; 15 | } 16 | 17 | logo { 18 | display: inline-block; 19 | height: 6rem; 20 | width: 6rem; 21 | background: #ecc835; 22 | margin-left: 1.5rem; 23 | border-radius: 0.5rem; 24 | color: #ffffff; 25 | font-weight: bold; 26 | float: left; 27 | } 28 | 29 | .logo-title { 30 | display: block; 31 | text-align: center; 32 | font-size: 2rem; 33 | line-height: 7vh; 34 | margin-top: 1rem; 35 | } 36 | 37 | .logo-info { 38 | display: block; 39 | text-align: center; 40 | } 41 | 42 | .feature-group { 43 | margin-left: 26vh; 44 | font-size: 0; 45 | color: #ffffff; 46 | } 47 | 48 | score { 49 | display: block; 50 | } 51 | 52 | score > span { 53 | background: #b7a99e; 54 | box-sizing: border-box; 55 | width: 5rem; 56 | height: 3rem; 57 | border-radius: 0.3rem; 58 | text-align: center; 59 | font-size: 0.9rem; 60 | } 61 | 62 | .score-now { 63 | display: inline-block; 64 | /*height: calc(7vh + 3vh + 1vh);*/ 65 | } 66 | 67 | .score-max > span, 68 | .score-now > span { 69 | display: block; 70 | } 71 | 72 | .score-max { 73 | display: inline-block; 74 | margin-left: 7vw; 75 | } 76 | 77 | .score-info { 78 | margin-top: 0.2rem 79 | } 80 | 81 | #id-score-now, 82 | #id-score-max { 83 | font-size: 1.4rem; 84 | } 85 | 86 | feature { 87 | display: block; 88 | margin-top: 0.5rem; 89 | } 90 | 91 | feature > span { 92 | background: #f5965f; 93 | width: 5rem; 94 | height: 2rem; 95 | line-height: 2rem; 96 | border-radius: 0.3rem; 97 | text-align: center; 98 | font-size: 0.9rem; 99 | } 100 | 101 | .feature-undo { 102 | display: inline-block; 103 | } 104 | 105 | .new-game { 106 | display: inline-block; 107 | margin-left: 7vw; 108 | } 109 | 110 | /* footer */ 111 | .t2048-footer { 112 | height: 15%; 113 | } 114 | 115 | /* 2048 main */ 116 | .t2048-main { 117 | height: 60%; 118 | } 119 | 120 | table { 121 | width: 90vw; 122 | height: 90vw; 123 | background: #b9ac9f; 124 | margin: auto; 125 | overflow: hidden; 126 | } 127 | 128 | td { 129 | background: #cabeb1; 130 | border-radius: 0.2rem; 131 | width: 25%; 132 | height: 25%; 133 | text-align: center; 134 | font-size: 3rem; 135 | font-weight: 800; 136 | } 137 | 138 | td > span { 139 | display: inline-block; 140 | width: 100%; 141 | height: 100%; 142 | /* 算出行高的实际值 */ 143 | line-height: 20vw; 144 | border-radius: 0.5rem; 145 | } 146 | 147 | /* 垂直居中 */ 148 | .vertical-align { 149 | position: relative; 150 | top: 50%; 151 | transform: translateY(-50%); 152 | } 153 | 154 | .t2 { 155 | background: #ebe2d8; 156 | color: #756d63; 157 | } 158 | 159 | .t4 { 160 | background: #eaddc6; 161 | color: #756d63; 162 | } 163 | 164 | .t8 { 165 | background: #efaf77; 166 | color: #f8f5f1; 167 | } 168 | 169 | .t16 { 170 | background: #f39463; 171 | color: #f8f5f1; 172 | } 173 | 174 | .t32 { 175 | background: #f37a5e; 176 | color: #f8f5f1; 177 | } 178 | 179 | .t64 { 180 | background: #f45d3a; 181 | color: #f8f5f0; 182 | } 183 | 184 | .t128 { 185 | background: #edcf72; 186 | color: #f8f5f0; 187 | font-size: 2.8rem; 188 | } 189 | 190 | .t256 { 191 | background: #edcb5f; 192 | color: #f9f6f3; 193 | font-size: 2.8rem; 194 | } 195 | 196 | .t512 { 197 | background: #edc850; 198 | color: #f8f5f1; 199 | font-size: 2.8rem; 200 | } 201 | 202 | .t1024 { 203 | background: #edc53f; 204 | color: #f8f5f3; 205 | font-size: 2rem; 206 | } 207 | 208 | .t2048 { 209 | background: #3e3c31; 210 | color: #f8f5f0; 211 | font-size: 2rem; 212 | } 213 | 214 | .t4096 { 215 | background: #2b2a23; 216 | color: #f8f5f0; 217 | font-size: 2rem; 218 | } 219 | 220 | .t8192 { 221 | background: #21201b; 222 | color: #f8f5f0; 223 | font-size: 2rem; 224 | } 225 | 226 | /* 新添加的元素添加放大效果 */ 227 | .new-one { 228 | transform: scale(0); 229 | animation: shine 0.5s 0.25s; 230 | -webkit-animation: shine 0.5s 0.25s; 231 | } 232 | 233 | @keyframes shine { 234 | from { 235 | transform: scale(0.2); 236 | -webkit-transform: scale(0.2); 237 | } 238 | to { 239 | transform: scale(1); 240 | -webkit-transform: scale(1); 241 | } 242 | } 243 | 244 | /* 合并的元素添加缩小效果 */ 245 | .merge-one { 246 | animation: suo 0.5s; 247 | -webkit-animation: suo 0.5s; 248 | } 249 | 250 | @keyframes suo { 251 | from { 252 | transform: scale(2); 253 | -webkit-transform: scale(2); 254 | } 255 | to { 256 | transform: scale(1); 257 | -webkit-transform: scale(1); 258 | } 259 | } 260 | 261 | .none { 262 | font-size: 0 !important; 263 | } 264 | 265 | .success-eff, 266 | .false-eff { 267 | text-align: center; 268 | font-size: 2rem; 269 | margin: 0 1rem; 270 | font-weight: bolder; 271 | color: #f54444; 272 | } 273 | 274 | /* warning 动画 */ 275 | .warning { 276 | color: #f54444; 277 | -webkit-animation: 0.1s warn 4 alternate; 278 | animation: 0.1s warn 4 alternate; 279 | text-align: center; 280 | } 281 | 282 | @keyframes warn { 283 | 0% { 284 | transform: translate(-5%, 0%); 285 | } 286 | 50% { 287 | transform: translate(5%, 0%); 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /** 4 | * 1. Change the default font family in all browsers (opinionated). 5 | * 2. Correct the line height in all browsers. 6 | * 3. Prevent adjustments of font size after orientation changes in 7 | * IE on Windows Phone and in iOS. 8 | */ 9 | 10 | /* Document 11 | ========================================================================== */ 12 | 13 | html { 14 | font-family: sans-serif; /* 1 */ 15 | line-height: 1.15; /* 2 */ 16 | -ms-text-size-adjust: 100%; /* 3 */ 17 | -webkit-text-size-adjust: 100%; /* 3 */ 18 | } 19 | 20 | /* Sections 21 | ========================================================================== */ 22 | 23 | /** 24 | * Remove the margin in all browsers (opinionated). 25 | */ 26 | 27 | body { 28 | margin: 0; 29 | } 30 | 31 | /** 32 | * Add the correct display in IE 9-. 33 | */ 34 | 35 | article, 36 | aside, 37 | footer, 38 | header, 39 | nav, 40 | section { 41 | display: block; 42 | } 43 | 44 | /** 45 | * Correct the font size and margin on `h1` elements within `section` and 46 | * `article` contexts in Chrome, Firefox, and Safari. 47 | */ 48 | 49 | h1 { 50 | font-size: 2em; 51 | margin: 0.67em 0; 52 | } 53 | 54 | /* Grouping content 55 | ========================================================================== */ 56 | 57 | /** 58 | * Add the correct display in IE 9-. 59 | * 1. Add the correct display in IE. 60 | */ 61 | 62 | figcaption, 63 | figure, 64 | main { /* 1 */ 65 | display: block; 66 | } 67 | 68 | /** 69 | * Add the correct margin in IE 8. 70 | */ 71 | 72 | figure { 73 | margin: 1em 40px; 74 | } 75 | 76 | /** 77 | * 1. Add the correct box sizing in Firefox. 78 | * 2. Show the overflow in Edge and IE. 79 | */ 80 | 81 | hr { 82 | box-sizing: content-box; /* 1 */ 83 | height: 0; /* 1 */ 84 | overflow: visible; /* 2 */ 85 | } 86 | 87 | /** 88 | * 1. Correct the inheritance and scaling of font size in all browsers. 89 | * 2. Correct the odd `em` font sizing in all browsers. 90 | */ 91 | 92 | pre { 93 | font-family: monospace, monospace; /* 1 */ 94 | font-size: 1em; /* 2 */ 95 | } 96 | 97 | /* Text-level semantics 98 | ========================================================================== */ 99 | 100 | /** 101 | * 1. Remove the gray background on active links in IE 10. 102 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. 103 | */ 104 | 105 | a { 106 | background-color: transparent; /* 1 */ 107 | -webkit-text-decoration-skip: objects; /* 2 */ 108 | } 109 | 110 | /** 111 | * Remove the outline on focused links when they are also active or hovered 112 | * in all browsers (opinionated). 113 | */ 114 | 115 | a:active, 116 | a:hover { 117 | outline-width: 0; 118 | } 119 | 120 | /** 121 | * 1. Remove the bottom border in Firefox 39-. 122 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 123 | */ 124 | 125 | abbr[title] { 126 | border-bottom: none; /* 1 */ 127 | text-decoration: underline; /* 2 */ 128 | text-decoration: underline dotted; /* 2 */ 129 | } 130 | 131 | /** 132 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6. 133 | */ 134 | 135 | b, 136 | strong { 137 | font-weight: inherit; 138 | } 139 | 140 | /** 141 | * Add the correct font weight in Chrome, Edge, and Safari. 142 | */ 143 | 144 | b, 145 | strong { 146 | font-weight: bolder; 147 | } 148 | 149 | /** 150 | * 1. Correct the inheritance and scaling of font size in all browsers. 151 | * 2. Correct the odd `em` font sizing in all browsers. 152 | */ 153 | 154 | code, 155 | kbd, 156 | samp { 157 | font-family: monospace, monospace; /* 1 */ 158 | font-size: 1em; /* 2 */ 159 | } 160 | 161 | /** 162 | * Add the correct font style in Android 4.3-. 163 | */ 164 | 165 | dfn { 166 | font-style: italic; 167 | } 168 | 169 | /** 170 | * Add the correct background and color in IE 9-. 171 | */ 172 | 173 | mark { 174 | background-color: #ff0; 175 | color: #000; 176 | } 177 | 178 | /** 179 | * Add the correct font size in all browsers. 180 | */ 181 | 182 | small { 183 | font-size: 80%; 184 | } 185 | 186 | /** 187 | * Prevent `sub` and `sup` elements from affecting the line height in 188 | * all browsers. 189 | */ 190 | 191 | sub, 192 | sup { 193 | font-size: 75%; 194 | line-height: 0; 195 | position: relative; 196 | vertical-align: baseline; 197 | } 198 | 199 | sub { 200 | bottom: -0.25em; 201 | } 202 | 203 | sup { 204 | top: -0.5em; 205 | } 206 | 207 | /* Embedded content 208 | ========================================================================== */ 209 | 210 | /** 211 | * Add the correct display in IE 9-. 212 | */ 213 | 214 | audio, 215 | video { 216 | display: inline-block; 217 | } 218 | 219 | /** 220 | * Add the correct display in iOS 4-7. 221 | */ 222 | 223 | audio:not([controls]) { 224 | display: none; 225 | height: 0; 226 | } 227 | 228 | /** 229 | * Remove the border on images inside links in IE 10-. 230 | */ 231 | 232 | img { 233 | border-style: none; 234 | } 235 | 236 | /** 237 | * Hide the overflow in IE. 238 | */ 239 | 240 | svg:not(:root) { 241 | overflow: hidden; 242 | } 243 | 244 | /* Forms 245 | ========================================================================== */ 246 | 247 | /** 248 | * 1. Change the font styles in all browsers (opinionated). 249 | * 2. Remove the margin in Firefox and Safari. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | font-family: sans-serif; /* 1 */ 258 | font-size: 100%; /* 1 */ 259 | line-height: 1.15; /* 1 */ 260 | margin: 0; /* 2 */ 261 | } 262 | 263 | /** 264 | * Show the overflow in IE. 265 | * 1. Show the overflow in Edge. 266 | */ 267 | 268 | button, 269 | input { /* 1 */ 270 | overflow: visible; 271 | } 272 | 273 | /** 274 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 275 | * 1. Remove the inheritance of text transform in Firefox. 276 | */ 277 | 278 | button, 279 | select { /* 1 */ 280 | text-transform: none; 281 | } 282 | 283 | /** 284 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` 285 | * controls in Android 4. 286 | * 2. Correct the inability to style clickable types in iOS and Safari. 287 | */ 288 | 289 | button, 290 | html [type="button"], /* 1 */ 291 | [type="reset"], 292 | [type="submit"] { 293 | -webkit-appearance: button; /* 2 */ 294 | } 295 | 296 | /** 297 | * Remove the inner border and padding in Firefox. 298 | */ 299 | 300 | button::-moz-focus-inner, 301 | [type="button"]::-moz-focus-inner, 302 | [type="reset"]::-moz-focus-inner, 303 | [type="submit"]::-moz-focus-inner { 304 | border-style: none; 305 | padding: 0; 306 | } 307 | 308 | /** 309 | * Restore the focus styles unset by the previous rule. 310 | */ 311 | 312 | button:-moz-focusring, 313 | [type="button"]:-moz-focusring, 314 | [type="reset"]:-moz-focusring, 315 | [type="submit"]:-moz-focusring { 316 | outline: 1px dotted ButtonText; 317 | } 318 | 319 | /** 320 | * Change the border, margin, and padding in all browsers (opinionated). 321 | */ 322 | 323 | fieldset { 324 | border: 1px solid #c0c0c0; 325 | margin: 0 2px; 326 | padding: 0.35em 0.625em 0.75em; 327 | } 328 | 329 | /** 330 | * 1. Correct the text wrapping in Edge and IE. 331 | * 2. Correct the color inheritance from `fieldset` elements in IE. 332 | * 3. Remove the padding so developers are not caught out when they zero out 333 | * `fieldset` elements in all browsers. 334 | */ 335 | 336 | legend { 337 | box-sizing: border-box; /* 1 */ 338 | color: inherit; /* 2 */ 339 | display: table; /* 1 */ 340 | max-width: 100%; /* 1 */ 341 | padding: 0; /* 3 */ 342 | white-space: normal; /* 1 */ 343 | } 344 | 345 | /** 346 | * 1. Add the correct display in IE 9-. 347 | * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. 348 | */ 349 | 350 | progress { 351 | display: inline-block; /* 1 */ 352 | vertical-align: baseline; /* 2 */ 353 | } 354 | 355 | /** 356 | * Remove the default vertical scrollbar in IE. 357 | */ 358 | 359 | textarea { 360 | overflow: auto; 361 | } 362 | 363 | /** 364 | * 1. Add the correct box sizing in IE 10-. 365 | * 2. Remove the padding in IE 10-. 366 | */ 367 | 368 | [type="checkbox"], 369 | [type="radio"] { 370 | box-sizing: border-box; /* 1 */ 371 | padding: 0; /* 2 */ 372 | } 373 | 374 | /** 375 | * Correct the cursor style of increment and decrement buttons in Chrome. 376 | */ 377 | 378 | [type="number"]::-webkit-inner-spin-button, 379 | [type="number"]::-webkit-outer-spin-button { 380 | height: auto; 381 | } 382 | 383 | /** 384 | * 1. Correct the odd appearance in Chrome and Safari. 385 | * 2. Correct the outline style in Safari. 386 | */ 387 | 388 | [type="search"] { 389 | -webkit-appearance: textfield; /* 1 */ 390 | outline-offset: -2px; /* 2 */ 391 | } 392 | 393 | /** 394 | * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. 395 | */ 396 | 397 | [type="search"]::-webkit-search-cancel-button, 398 | [type="search"]::-webkit-search-decoration { 399 | -webkit-appearance: none; 400 | } 401 | 402 | /** 403 | * 1. Correct the inability to style clickable types in iOS and Safari. 404 | * 2. Change font properties to `inherit` in Safari. 405 | */ 406 | 407 | ::-webkit-file-upload-button { 408 | -webkit-appearance: button; /* 1 */ 409 | font: inherit; /* 2 */ 410 | } 411 | 412 | /* Interactive 413 | ========================================================================== */ 414 | 415 | /* 416 | * Add the correct display in IE 9-. 417 | * 1. Add the correct display in Edge, IE, and Firefox. 418 | */ 419 | 420 | details, /* 1 */ 421 | menu { 422 | display: block; 423 | } 424 | 425 | /* 426 | * Add the correct display in all browsers. 427 | */ 428 | 429 | summary { 430 | display: list-item; 431 | } 432 | 433 | /* Scripting 434 | ========================================================================== */ 435 | 436 | /** 437 | * Add the correct display in IE 9-. 438 | */ 439 | 440 | canvas { 441 | display: inline-block; 442 | } 443 | 444 | /** 445 | * Add the correct display in IE. 446 | */ 447 | 448 | template { 449 | display: none; 450 | } 451 | 452 | /* Hidden 453 | ========================================================================== */ 454 | 455 | /** 456 | * Add the correct display in IE 10-. 457 | */ 458 | 459 | [hidden] { 460 | display: none; 461 | } 462 | -------------------------------------------------------------------------------- /2048.js: -------------------------------------------------------------------------------- 1 | const e = (sel) => document.querySelector(sel) 2 | 3 | const es = (sel) => document.querySelectorAll(sel) 4 | 5 | const arrayFormatByTds = function(table) { 6 | /* 7 | 传入一个 table 标签,将 table 里 的 tds > span 生成一个 二维数组 8 | */ 9 | var tds = es(table+' td > span') 10 | var trs = es(table+' tr') 11 | // console.log(tds, trs); 12 | var t = t2048.arrayInit(trs.length) 13 | for (var i = 0; i < trs.length; i++) { 14 | for (var j = 0; j < trs[i].children.length; j++) { 15 | t[i][j] = tds[i * trs.length + j] 16 | } 17 | } 18 | return t 19 | } 20 | 21 | const changeSingleView = function(td, value) { 22 | /* 23 | 改变单个单元格 td 的 view 24 | */ 25 | td.setAttribute('class', '') 26 | if (value != 0) { 27 | var className = 't' + value 28 | td.classList.add(className) 29 | td.innerHTML = value 30 | } else { 31 | td.innerHTML = '' 32 | } 33 | } 34 | 35 | const changeViewByArray = function(table, array) { 36 | /* 37 | table 是 包括 tds 的二维素组 38 | 将 View 的 value 变成 array 中的值 39 | */ 40 | for (var i = 0; i < array.length; i++) { 41 | for (var j = 0; j < array[i].length; j++) { 42 | var value = array[i][j] 43 | var td = table[i][j] 44 | changeSingleView(td, value) 45 | } 46 | } 47 | } 48 | 49 | const showisSuccess = function(status) { 50 | // console.log('vix', status); 51 | if(status.false) { 52 | // console.log('game false'); 53 | e('.false-eff').classList.remove('none') 54 | e('.false-eff').classList.add('warning') 55 | setTimeout.call(this, function(){ 56 | e('.false-eff').classList.remove('warning') 57 | }, 400) 58 | } else { 59 | e('.false-eff').classList.add('none') 60 | } 61 | if(status.success) { 62 | e('.success-eff').classList.remove('none') 63 | e('.success-eff').classList.add('warning') 64 | setTimeout.call(this, function(){ 65 | e('.success-eff').classList.remove('warning') 66 | }, 400) 67 | } else { 68 | e('.success-eff').classList.add('none') 69 | } 70 | } 71 | 72 | const showScore = function(score) { 73 | e('#id-score-now').innerHTML = score.now 74 | var nowMax = e('#id-score-max').innerHTML 75 | // if (score.now > nowMax) { 76 | // e('#id-score-max').innerHTML = score.max 77 | // } 78 | e('#id-score-max').innerHTML = score.max 79 | 80 | } 81 | 82 | const showMerge = function(array) { 83 | /* 84 | 给合并元素添加 merge-one css 动画 85 | */ 86 | var table = arrayFormatByTds('table') 87 | for (var i = 0; i < array.length; i++) { 88 | var x = array[i][0] 89 | var y = array[i][1] 90 | table[x][y].classList.add('merge-one') 91 | setTimeout.call(this, function(){ 92 | table[x][y].classList.remove('merge-one') 93 | }, 500) 94 | } 95 | } 96 | 97 | const showView = function(TZFE) { 98 | /* 99 | 把 array 二维数组 和 分数 和 成功/失败 渲染成页面,并备份 100 | */ 101 | var a = TZFE.copyArray(TZFE.value) 102 | var table = arrayFormatByTds('table') 103 | changeViewByArray(table, a) 104 | // console.log('show view', TZFE); 105 | showScore(TZFE.score) 106 | showisSuccess(TZFE.status) 107 | showMerge(TZFE.merge) 108 | showNew(TZFE.new) 109 | } 110 | 111 | // const showNew = function(i, j) { 112 | // // console.log('new one', i, j); 113 | // var t = arrayFormatByTds('table') 114 | // // t[i][j].classList.remove('new-one') 115 | // t[i][j].classList.add('new-one') 116 | // setTimeout.call(this, function(){ 117 | // t[i][j].classList.remove('new-one') 118 | // }, 500) 119 | // } 120 | 121 | const showNew = function(array) { 122 | /* 123 | array 格式 [[x1, y1], [x2, y2]...] 124 | */ 125 | // console.log('showNew', array); 126 | var t = arrayFormatByTds('table') 127 | for (var i = 0; i < array.length; i++) { 128 | var x = array[i][0] 129 | var y = array[i][1] 130 | if (x !== false) { 131 | t[x][y].classList.add('new-one') 132 | setTimeout.call(this, function(){ 133 | t[x][y].classList.remove('new-one') 134 | }, 500) 135 | } 136 | } 137 | } 138 | 139 | const leftActionToView = function() { 140 | /* 141 | 向左滑动时视图的变化,并返回新元素的坐标 142 | */ 143 | var r = t2048.handleLeftArray(t2048.value) 144 | t2048.value = r.value 145 | showView(t2048) 146 | // console.log(value2048); 147 | return r 148 | } 149 | 150 | const rightActionToView = function() { 151 | /* 152 | 向右滑动时视图的变化,并返回新元素的坐标 153 | */ 154 | var r = t2048.handleRightArray(t2048.value) 155 | t2048.value = r.value 156 | showView(t2048) 157 | // console.log(value2048); 158 | return r 159 | } 160 | 161 | const upActionToView = function() { 162 | /* 163 | 向上滑动时视图的变化,并返回新元素的坐标 164 | */ 165 | var r = t2048.handleUpArray(t2048.value) 166 | t2048.value = r.value 167 | showView(t2048) 168 | // console.log(value2048); 169 | return r 170 | } 171 | 172 | const downActionToView = function() { 173 | /* 174 | 向下滑动时视图的变化,并返回新元素的坐标 175 | */ 176 | var r = t2048.handleDownArray(t2048.value) 177 | // console.log(r); 178 | t2048.value = r.value 179 | showView(t2048) 180 | // console.log(value2048); 181 | return r 182 | } 183 | 184 | const undo = function(TZFE) { 185 | var length = TZFE.backupData.length 186 | // console.log(length); 187 | if (length <= 1) { 188 | // console.log('<=1'); 189 | var b = undefined 190 | } else { 191 | TZFE.backupData.pop() 192 | var b = TZFE.backupData[length-2] 193 | } 194 | // console.log('backupData is', b, TZFE.score.now); 195 | if (b != undefined) { 196 | TZFE.score = { 197 | now: b.score.now, 198 | max: b.score.max, 199 | } 200 | TZFE.status = { 201 | success: b.status.success, 202 | false: b.status.false, 203 | } 204 | TZFE.value = b.value 205 | TZFE.length = b.length 206 | TZFE.merge = (b.merge == undefined) ? [] : b.merge 207 | TZFE.new = (b.new == undefined) ? [] : b.new 208 | // console.log('TZFE score now', TZFE.score.now); 209 | save2048(TZFE) 210 | showView(TZFE) 211 | } 212 | } 213 | 214 | const resetData = function(length) { 215 | var max = t2048.score.max 216 | t2048 = new TZFE(length) 217 | t2048.score.max = max 218 | t2048.value = false 219 | save2048(t2048) 220 | } 221 | 222 | const save2048 = function(TZFE) { 223 | var t = { 224 | score: TZFE.score, 225 | status: TZFE.status, 226 | value: TZFE.value, 227 | length: TZFE.length, 228 | backupData: TZFE.backupData, 229 | merge: TZFE.merge, 230 | new: TZFE.new 231 | } 232 | var data = JSON.stringify(t) 233 | localStorage.t2048 = data 234 | } 235 | 236 | const load2048 = function(TZFE) { 237 | var t = localStorage.t2048 238 | if (t) { 239 | data = JSON.parse(t) 240 | TZFE.score = data.score 241 | TZFE.status = data.status 242 | TZFE.value = data.value 243 | TZFE.length = data.length 244 | TZFE.backupData = (data.backupData == undefined) ? [] : data.backupData 245 | return true 246 | } else { 247 | console.log('无储存数据'); 248 | return false 249 | } 250 | } 251 | 252 | const init2048 = function(TZFE) { 253 | // console.log(TZFE); 254 | var flat = false 255 | if (!load2048(TZFE) || !TZFE.value) { 256 | flat = true 257 | var n = TZFE.init2048Array(TZFE.length) 258 | TZFE.value = n.initArray 259 | } 260 | // console.log(TZFE); 261 | showView(TZFE) 262 | if (flat) { 263 | TZFE.backup() 264 | // showNew(n.f.i, n.f.j) 265 | // showNew(n.s.i, n.s.j) 266 | } 267 | } 268 | 269 | const angleBySlide = function(dx, dy) { 270 | return Math.atan2(dy,dx) * 180 / Math.PI 271 | } 272 | 273 | const judgeDirection = function(sX, sY, eX, eY) { 274 | /* 275 | 根据坐标判断 方向 276 | return: false 为判断不出 277 | 'up' 为上 278 | 'down' 279 | 'right' 280 | 'left' 281 | */ 282 | var dx = eX - sX 283 | var dy = sY - eY 284 | var angle = angleBySlide(dx, dy); 285 | // 滑动距离太短 的情况 286 | if (Math.abs(dx) < 30 && Math.abs(dy) < 30) { 287 | return false 288 | } else if (angle >= -45 && angle < 45) { 289 | return 'right' 290 | } else if (angle >= 45 && angle < 135) { 291 | return 'up' 292 | }else if (angle >= -135 && angle < -45) { 293 | return 'down' 294 | }else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) { 295 | return 'left' 296 | } 297 | } 298 | 299 | const bindSlideEvent = function() { 300 | var startX, startY 301 | 302 | e('table').addEventListener('touchstart', function(event){ 303 | // console.log('touchstart', event); 304 | startX = event.touches[0].pageX 305 | startY = event.touches[0].pageY 306 | }) 307 | 308 | e('.t2048-main').addEventListener('touchmove', function(event){ 309 | // console.log('touchmove', event); 310 | // var endX = event.changedTouches[0].pageX; 311 | // var endY = event.changedTouches[0].pageY; 312 | event.preventDefault() 313 | }) 314 | 315 | e('table').addEventListener('touchend', function(event){ 316 | // console.log('touchend', event); 317 | var endX = event.changedTouches[0].pageX; 318 | var endY = event.changedTouches[0].pageY; 319 | 320 | var dire = judgeDirection(startX, startY, endX, endY) 321 | 322 | if (dire == 'up') { 323 | var r = upActionToView() 324 | } else if (dire == 'down') { 325 | var r = downActionToView() 326 | } else if (dire == 'left') { 327 | var r = leftActionToView() 328 | } else if (dire == 'right') { 329 | var r = rightActionToView() 330 | } else if (!dire) { 331 | return false 332 | } 333 | if (r.i !== false) { 334 | t2048.backup() 335 | // 给 新增加的元素 添加 放大 的动画 336 | save2048(t2048) 337 | // showNew(r.i, r.j) 338 | // console.log(t2048.merge); 339 | } 340 | }) 341 | } 342 | 343 | const newGame = function(length) { 344 | // value2048 = arrayInit(4) 345 | // score.now = 0 346 | // statusSuc.success = false 347 | // statusSuc.false = false 348 | resetData(length) 349 | e('.false-eff').classList.add('none') 350 | e('.success-eff').classList.add('none') 351 | init2048(t2048) 352 | } 353 | 354 | const bindNewGame = function() { 355 | e('.new-game').addEventListener('touchend', function(event){ 356 | newGame(t2048.length) 357 | }) 358 | } 359 | 360 | const bindUndo = function() { 361 | e('.feature-undo').addEventListener('touchend', function(event){ 362 | undo(t2048) 363 | }) 364 | } 365 | 366 | const bindEvents = function() { 367 | bindSlideEvent() 368 | 369 | bindNewGame() 370 | 371 | bindUndo() 372 | } 373 | 374 | var t2048 = new TZFE(4) 375 | 376 | const __main2048 = function() { 377 | init2048(t2048) 378 | 379 | bindEvents() 380 | } 381 | -------------------------------------------------------------------------------- /2048lib.js: -------------------------------------------------------------------------------- 1 | var TZFE = function(length) { 2 | this.score = { 3 | now: 0, 4 | max: 0, 5 | } 6 | this.status = { 7 | success: false, 8 | false: false, 9 | } 10 | this.value = this.arrayInit(length) 11 | this.length = length 12 | this.backupData = [] 13 | this.merge = [] 14 | this.new = [] 15 | } 16 | 17 | TZFE.prototype.saveScore = function(s) { 18 | /* 19 | 记录 分数 20 | */ 21 | this.score.now += s 22 | if (this.score.now > this.score.max) { 23 | this.score.max = this.score.now 24 | } 25 | // console.log(this.score.now, this.score.max); 26 | } 27 | 28 | TZFE.prototype.backup = function() { 29 | var data = { 30 | // score: new Object(this.score), 31 | score: { 32 | now: this.score.now, 33 | max: this.score.max, 34 | }, 35 | // status: new Object(this.status), 36 | status: { 37 | success: this.status.success, 38 | false: this.status.false, 39 | }, 40 | value: this.value, 41 | length: this.length, 42 | merge: this.merge, 43 | new: this.new, 44 | } 45 | // console.log('backup', data, data.score); 46 | // console.log(data.score === this.score); 47 | // console.log(data.status === this.status); 48 | // console.log(data.value === this.value); 49 | this.backupData.push(data) 50 | } 51 | 52 | TZFE.prototype.supplementZero = function(array, direction, num) { 53 | /* 54 | 给 array 补充 num 个 0 55 | direction 参数: begin 表示在头部补 0 56 | end 表示在尾部补 0 57 | */ 58 | var a = array.slice(0) 59 | if (direction == 'begin') { 60 | for (var i = 0; i < num; i++) { 61 | a.unshift(0) 62 | } 63 | return a 64 | } else if (direction == 'end') { 65 | for (var i = 0; i < num; i++) { 66 | a.push(0) 67 | } 68 | return a 69 | } 70 | } 71 | 72 | TZFE.prototype.rejectByIndex = function(array, index, direction='end') { 73 | /* 74 | 将 array[index] 剔除,并在头/尾补充 0,保持长度 75 | direction 参数: begin 表示在头部补 0 76 | end 表示在尾部补 0 77 | */ 78 | var a = array.slice(0) 79 | a.splice(index, 1) 80 | a = this.supplementZero(a, direction, 1) 81 | return a 82 | // if (index == 0) { 83 | // a = a.slice(1) 84 | // } else if (index == a.length - 1) { 85 | // a.pop() 86 | // } else if(index > 0 && index < a.length - 1) { 87 | // var qian = a.slice(0, index) 88 | // var hou = a.slice(index+1) 89 | // a = qian.concat(hou) 90 | // } 91 | // if (direction == 'begin') { 92 | // a.unshift(0) 93 | // } else if (direction == 'end') { 94 | // a.push(0) 95 | // } 96 | } 97 | 98 | TZFE.prototype.moveArray = function(array, direction) { 99 | /* 100 | 移动数组: 根据 direction 让 array 清除 左/右 边的 0 , 101 | 并在另一边用补 0 的方式保持 array 原来的长度 102 | direction 参数: left 表示清除 左 边的 0 , 103 | right 表示清除 右 边的 0 , 104 | */ 105 | var a = array.slice(0) 106 | var length = a.length 107 | var index = 0 108 | if (direction == 'left') { 109 | for (var i = 0; i < length; i++) { 110 | if(a[i] != 0) { 111 | index = i 112 | // console.log(index); 113 | a = a.slice(index) 114 | var num = length - a.length 115 | a = this.supplementZero(a, 'end', num) 116 | return a 117 | } 118 | } 119 | } else if(direction == 'right') { 120 | for (var i = 0; i < length; i++) { 121 | if(a[length - 1 - i] != 0) { 122 | index = length - 1 - i 123 | a = a.slice(0, index+1) 124 | // console.log(a, index); 125 | var num = length - a.length 126 | a = this.supplementZero(a, 'begin', num) 127 | return a 128 | } 129 | } 130 | } 131 | return a 132 | } 133 | 134 | TZFE.prototype.arrayByClearZero = function(array) { 135 | var a = array.slice(0) 136 | for (var i = 0; i < a.length; i++) { 137 | if(a[i] == 0) { 138 | a.splice(i, 1) 139 | i-- 140 | } 141 | } 142 | a = this.supplementZero(a, 'end', array.length-a.length) 143 | return a 144 | } 145 | 146 | TZFE.prototype.handleOneLine = function(array, direction) { 147 | /* 148 | 处理一行 149 | direction 参数: left 表示在向左滑动处理 150 | right 表示在向右滑动处理 151 | 返回处理好的数据 a & 合并位置的坐标 is 152 | */ 153 | var a = array.slice(0) 154 | a = this.arrayByClearZero(a) 155 | var is = [] 156 | var length = a.length 157 | a = this.moveArray(a, direction) 158 | for (var i = 0; i < length; i++) { 159 | // if (a[i] == a[i+1] && a[i] != 0) { 160 | // a[i] *= 2 161 | // if (direction == 'left') { 162 | // a = rejectByIndex(a, i+1, 'end') 163 | // } else if (direction == 'right') { 164 | // a = rejectByIndex(a, i+1, 'begin') 165 | // } 166 | // } 167 | if (direction == 'left') { 168 | if (a[i] == a[i+1] && a[i] != 0) { 169 | a[i] *= 2 170 | is.push(i) 171 | this.saveScore(a[i]) 172 | a = this.rejectByIndex(a, i+1, 'end') 173 | } 174 | } else if (direction == 'right') { 175 | if (a[length-1-i] == a[length-i-2] && a[length-1-i] != 0) { 176 | a[length-1-i] *= 2 177 | is.push(length-1-i) 178 | this.saveScore(a[length-1-i]) 179 | a = this.rejectByIndex(a, length-i-2, 'begin') 180 | } 181 | } 182 | } 183 | // console.log(a); 184 | // a = this.moveArray(a, direction) 185 | return { 186 | a: a, 187 | is: is, 188 | } 189 | } 190 | 191 | TZFE.prototype.encodeArray = function(array) { 192 | /* 193 | 旋转 array 194 | 使得 上下 变 左右 195 | */ 196 | // console.log('encodeArray'); 197 | var a = this.copyArray(array) 198 | var length = a.length 199 | var r = this.arrayInit(length) 200 | for (var i = 0; i < length; i++) { 201 | for (var j = 0; j < a[i].length; j++) { 202 | r[i][j] = array[j][i] 203 | } 204 | } 205 | return r 206 | } 207 | 208 | TZFE.prototype.decodeArray = function(array) { 209 | /* 210 | 复原 array 211 | 让 左右 回复成 上下 212 | */ 213 | // console.log('decodeArray'); 214 | return this.encodeArray(array) 215 | } 216 | 217 | TZFE.prototype.compareArray = function(a1, a2) { 218 | /* 219 | 比较两个 二维数组 是否相等 220 | */ 221 | for (var i = 0; i < a1.length; i++) { 222 | for (var j = 0; j < a1[i].length; j++) { 223 | if(a1[i][j] != a2[i][j]) { 224 | // console.log('不相等'); 225 | return false 226 | } 227 | } 228 | } 229 | // console.log('相等'); 230 | return true 231 | } 232 | 233 | TZFE.prototype.newOneOfArray = function(result, array) { 234 | /* 235 | 判断是否需要添加一个新元素,需要则添加 并 备份数据,不需要则返回{i:false, j:false} 236 | */ 237 | if(!this.compareArray(array, result)) { 238 | result = this.arrayByCreateZero(result) 239 | return result 240 | } else { 241 | return { 242 | value: result, 243 | i: false, 244 | j: false, 245 | } 246 | } 247 | } 248 | 249 | TZFE.prototype.isSameLine = function(array) { 250 | /* 251 | 判断 一维数组 是否有相邻 两个 是相等的,如果有则返回 true 252 | */ 253 | for (var i = 0; i < array.length-1; i++) { 254 | if(array[i] == array[i+1]) { 255 | return true 256 | } 257 | } 258 | return false 259 | } 260 | 261 | TZFE.prototype.isSuccess = function(array) { 262 | /* 263 | 判断 成功 还是 失败, 从而改变全局变量 status 264 | */ 265 | // 判断是否 成功 266 | for (var i = 0; i < array.length; i++) { 267 | for (var j = 0; j < array[i].length; j++) { 268 | if(array[i][j] == 2048) { 269 | this.status.success = true 270 | console.log('成功'); 271 | return 1 272 | } 273 | } 274 | } 275 | 276 | // 判断是否有有 空格 存在 277 | for (var i = 0; i < array.length; i++) { 278 | for (var j = 0; j < array[i].length; j++) { 279 | if(array[i][j] == 0) { 280 | return 1 281 | } 282 | } 283 | } 284 | 285 | // 判断每行相邻是否有相同的值 286 | for (var i = 0; i < array.length; i++) { 287 | if (this.isSameLine(array[i])) { 288 | return 1 289 | } 290 | } 291 | 292 | // 判断每列相邻是否有相同的值 293 | var a = this.encodeArray(array) 294 | for (var i = 0; i < a.length; i++) { 295 | if (this.isSameLine(a[i])) { 296 | return 1 297 | } 298 | } 299 | 300 | this.status.false = true 301 | console.log('失败'); 302 | } 303 | 304 | TZFE.prototype.saveMerge = function(i, js) { 305 | /* 306 | 将 js 拆成 j1 j2 ...,并和 i 组合成 [i, j] 保存到 this.merge 307 | */ 308 | for (var x = 0; x < js.length; x++) { 309 | var r = [] 310 | r.push(i) 311 | r.push(js[x]) 312 | this.merge.push(r) 313 | } 314 | } 315 | 316 | TZFE.prototype.handleLeftArray = function(array) { 317 | /* 318 | 向左滑动时 array 合并 & 移动 & 添加一个新元素 (2) 319 | 返回 r [value, i, j] (其中 i j 是增加的新元素的坐标) 320 | 并将 合并的坐标 保存到 this.merge 321 | */ 322 | this.merge = [] 323 | this.new = [] 324 | var a = this.copyArray(array) 325 | var r = [] 326 | for (var i = 0; i < a.length; i++) { 327 | var restlt = this.handleOneLine(a[i], 'left') 328 | r.push(restlt.a) 329 | this.saveMerge(i, restlt.is) 330 | } 331 | this.isSuccess(r) 332 | var restlt = this.newOneOfArray(r, array) 333 | var zuobiao = [restlt.i, restlt.j] 334 | this.new.push(zuobiao) 335 | return restlt 336 | } 337 | 338 | TZFE.prototype.handleRightArray = function(array) { 339 | /* 340 | 向右滑动时 array 合并 & 移动 & 添加一个新元素 (2) 341 | 返回 r [value, i, j] (其中 i j 是增加的新元素的坐标) 342 | 并将 合并的坐标 保存到 this.merge 343 | 将 新加的坐标 保存到 this.new 344 | */ 345 | // console.log('handleRightArray', array); 346 | this.merge = [] 347 | this.new = [] 348 | var a = this.copyArray(array) 349 | var r = [] 350 | for (var i = 0; i < a.length; i++) { 351 | var restlt = this.handleOneLine(a[i], 'right') 352 | r.push(restlt.a) 353 | this.saveMerge(i, restlt.is) 354 | // r.push(this.handleOneLine(a[i], 'right')) 355 | } 356 | this.isSuccess(r) 357 | var restlt = this.newOneOfArray(r, array) 358 | var zuobiao = [restlt.i, restlt.j] 359 | this.new.push(zuobiao) 360 | return restlt 361 | } 362 | 363 | // TZFE.prototype.encodeMerge = function() { 364 | // var a = this.merge 365 | // for (var i = 0; i < a.length; i++) { 366 | // var x = a[i][0] 367 | // var y = a[i][1] 368 | // a[i][0] = y 369 | // a[i][1] = x 370 | // } 371 | // } 372 | 373 | TZFE.prototype.encodeZuobiao = function(zuobiaoArrray) { 374 | var a = zuobiaoArrray 375 | for (var i = 0; i < a.length; i++) { 376 | var x = a[i][0] 377 | var y = a[i][1] 378 | a[i][0] = y 379 | a[i][1] = x 380 | } 381 | } 382 | 383 | TZFE.prototype.handleUpArray = function(array) { 384 | /* 385 | 向上滑动时 array 合并 & 移动 & 添加一个新元素 (2) 386 | 返回 r {value, i, j} (其中 i j 是增加的新元素的坐标) 387 | */ 388 | var a = this.copyArray(array) 389 | a = this.encodeArray(a) 390 | a = this.handleLeftArray(a) 391 | var r = {} 392 | r.value = this.decodeArray(a.value) 393 | r.i = a.j 394 | r.j = a.i 395 | this.encodeZuobiao(this.merge) 396 | this.encodeZuobiao(this.new) 397 | return r 398 | } 399 | 400 | TZFE.prototype.handleDownArray = function(array) { 401 | /* 402 | 向下滑动时 array 合并 & 移动 & 添加一个新元素 (2) 403 | 返回 r {value, i, j} (其中 i j 是增加的新元素的坐标) 404 | */ 405 | // console.log('handleDownArray'); 406 | var a = this.copyArray(array) 407 | a = this.encodeArray(a) 408 | a = this.handleRightArray(a) 409 | var r = {} 410 | r.value = this.decodeArray(a.value) 411 | r.i = a.j 412 | r.j = a.i 413 | this.encodeZuobiao(this.merge) 414 | this.encodeZuobiao(this.new) 415 | return r 416 | } 417 | 418 | TZFE.prototype.arrayLine = function(length) { 419 | // 制造一行 0 420 | var array = [] 421 | for (var i = 0; i < length; i++) { 422 | array.push(0) 423 | } 424 | return array 425 | } 426 | 427 | TZFE.prototype.arrayInit = function(length) { 428 | /* 429 | 制造一个 length * length 的矩阵数组, 矩阵数值为 0 430 | */ 431 | var array = [] 432 | for (var i = 0; i < length; i++) { 433 | array.push(this.arrayLine(length)) 434 | } 435 | return array 436 | } 437 | 438 | TZFE.prototype.numOfZeroFromArray = function(array) { 439 | /* 440 | 返回: 二维数组中 0 的个数 441 | */ 442 | var num = 0 443 | for (var i = 0; i < array.length; i++) { 444 | for (var j = 0; j < array[i].length; j++) { 445 | if(array[i][j] == 0) { 446 | num++ 447 | } 448 | } 449 | } 450 | return num 451 | } 452 | 453 | TZFE.prototype.numberRandom = function(count) { 454 | /* 455 | 在 0 ~ count 内,产生一个 随机数 456 | */ 457 | var r = Math.random() * count 458 | return Math.ceil(r) 459 | } 460 | 461 | TZFE.prototype.copyArray = function(array) { 462 | /* 463 | 复制二维数组 464 | */ 465 | // console.log('copyArray'); 466 | var r = [] 467 | for (var i = 0; i < array.length; i++) { 468 | r.push(array[i].slice(0)) 469 | } 470 | return r 471 | } 472 | 473 | TZFE.prototype.randomInitValue = function() { 474 | var a = Math.random() * 10 475 | if (a < 8) { 476 | return 2 477 | } else { 478 | return 4 479 | } 480 | } 481 | 482 | TZFE.prototype.arrayByCreateZero = function(array) { 483 | /* 484 | 随机挑选 二维数组 中的其中一个 0 位置赋值为 2,并返回这个 二维数组 & 坐标 485 | */ 486 | var a = this.copyArray(array) 487 | var count = this.numOfZeroFromArray(a) 488 | var num = this.numberRandom(count) 489 | var n = 0 490 | var initNum = this.randomInitValue() 491 | for (var i = 0; i < a.length; i++) { 492 | for (var j = 0; j < a[i].length; j++) { 493 | if(a[i][j] == 0) { 494 | n++ 495 | } 496 | if(num == n) { 497 | a[i][j] = initNum 498 | // console.log('lib new', i, j); 499 | return { 500 | value: a, 501 | i: i, 502 | j: j, 503 | } 504 | } 505 | } 506 | } 507 | } 508 | 509 | TZFE.prototype.init2048Array = function(length) { 510 | /* 511 | 初始化 2048,即生成一个二维数组,随机数组中有两个 2 ,返回 二维数组 & 坐标 512 | */ 513 | var initaArray = this.arrayInit(length) 514 | var r = this.arrayByCreateZero(initaArray) 515 | initArray = r.value 516 | var result = { 517 | f: { 518 | i: r.i, 519 | j: r.j, 520 | } 521 | } 522 | r = this.arrayByCreateZero(initArray) 523 | result.initArray = r.value 524 | result.s = { 525 | i: r.i, 526 | j: r.j, 527 | } 528 | return result 529 | } 530 | --------------------------------------------------------------------------------