1.游戏说明
使用方向键移动方块,相同数字的方块会合并。目标是创造出2048方块!
2.游戏界面
3.游戏代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2048 - 终极版AI算法</title>
<style>
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
margin: 0;
padding: 20px;
height: 100vh;
color: white;
display: flex;
flex-direction: column;
overflow: hidden;
box-sizing: border-box;
}
/* 真实视频背景 */
.video-background {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -2;
overflow: hidden;
}
.video-background video {
position: absolute;
top: 50%;
left: 50%;
min-width: 100%;
min-height: 100%;
width: auto;
height: auto;
transform: translateX(-50%) translateY(-50%);
object-fit: cover;
/* 优化视频质量 */
filter: contrast(1.1) brightness(0.9) saturate(1.2);
}
/* 备用背景 */
.fallback-background {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -3;
background: linear-gradient(45deg, #667eea, #764ba2, #f093fb, #f5576c);
background-size: 400% 400%;
animation: fallbackAnimation 15s ease infinite;
}
@keyframes fallbackAnimation {
0% {
background-position: 0% 50%;
filter: hue-rotate(0deg) brightness(1);
}
25% {
background-position: 100% 50%;
filter: hue-rotate(90deg) brightness(1.2);
}
50% {
background-position: 100% 100%;
filter: hue-rotate(180deg) brightness(0.8);
}
75% {
background-position: 0% 100%;
filter: hue-rotate(270deg) brightness(1.1);
}
100% {
background-position: 0% 50%;
filter: hue-rotate(360deg) brightness(1);
}
}
/* 视频遮罩层 */
.video-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
background: rgba(0, 0, 0, 0.4);
pointer-events: none;
}
/* 游戏头部栏:得分、标题和控制按钮 */
.game-header-bar {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
gap: 20px;
margin-bottom: 15px;
}
.game-info {
display: flex;
gap: 20px;
font-size: 18px;
font-weight: bold;
}
.game-title {
text-align: center;
margin-bottom: 15px;
position: relative;
padding: 15px 0;
perspective: 1000px;
}
.game-title h1 {
font-size: 4em;
font-weight: 900;
font-family: 'Impact', 'Arial Black', sans-serif;
margin: 0;
position: relative;
letter-spacing: 4px;
text-transform: uppercase;
transform-style: preserve-3d;
animation: titleFloat 4s ease-in-out infinite;
background: linear-gradient(45deg,
#ff0080, #ff8c00, #ffd700, #00ff80, #0080ff, #8000ff, #ff0080);
background-size: 400% 400%;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: neonFlow 3s linear infinite, titleFloat 4s ease-in-out infinite;
filter: drop-shadow(0 0 10px rgba(255, 0, 128, 0.8)) drop-shadow(0 0 20px rgba(255, 140, 0, 0.6)) drop-shadow(0 0 30px rgba(255, 215, 0, 0.4));
}
.game-title h1::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg,
rgba(255, 215, 0, 0.3) 0%,
rgba(255, 237, 78, 0.2) 50%,
rgba(255, 215, 0, 0.3) 100%);
border-radius: 15px;
filter: blur(20px);
z-index: -1;
animation: titleGlow 2s ease-in-out infinite alternate;
}
.game-title h1::after {
content: '2048 - 终极版';
position: absolute;
top: 0;
left: 0;
right: 0;
background: linear-gradient(135deg,
rgba(255, 215, 0, 0.1) 0%,
rgba(255, 237, 78, 0.05) 50%,
rgba(255, 215, 0, 0.1) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
z-index: -1;
transform: translate(2px, 2px);
filter: blur(1px);
}
/* 标题动画效果 */
@keyframes titleShine {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
@keyframes titleGlow {
0% {
opacity: 0.3;
transform: scale(0.95);
}
100% {
opacity: 0.6;
transform: scale(1.05);
}
}
/* 标题装饰元素 */
.game-title::before {
content: '✨';
position: absolute;
left: 20%;
top: 50%;
transform: translateY(-50%);
font-size: 1.5em;
animation: sparkle 2s ease-in-out infinite;
opacity: 0.8;
}
.game-title::after {
content: '✨';
position: absolute;
right: 20%;
top: 50%;
transform: translateY(-50%);
font-size: 1.5em;
animation: sparkle 2s ease-in-out infinite 1s;
opacity: 0.8;
}
@keyframes sparkle {
0%,
100% {
opacity: 0.4;
transform: translateY(-50%) scale(1);
}
50% {
opacity: 1;
transform: translateY(-50%) scale(1.2);
}
}
/* 新的炫酷动画效果 */
@keyframes neonFlow {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
@keyframes titleFloat {
0%,
100% {
transform: translateY(0px) rotateX(0deg);
}
25% {
transform: translateY(-8px) rotateX(3deg);
}
50% {
transform: translateY(0px) rotateX(0deg);
}
75% {
transform: translateY(8px) rotateX(-3deg);
}
}
/* 响应式标题 */
@media (max-width: 768px) {
.game-title h1 {
font-size: 2.5em;
letter-spacing: 1px;
}
.game-title::before,
.game-title::after {
font-size: 1.2em;
}
}
@media (max-width: 480px) {
.game-title h1 {
font-size: 2em;
letter-spacing: 0.5px;
}
.game-title::before,
.game-title::after {
display: none;
}
}
/* 合并的得分和控制区域 */
.game-info-controls-bar {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
max-width: 600px;
margin-bottom: 20px;
padding: 10px 0;
}
.game-score-section {
display: flex;
gap: 12px;
align-items: center;
}
.game-controls-section {
display: flex;
gap: 6px;
align-items: center;
flex-wrap: wrap;
justify-content: center;
}
/* 游戏控制区域样式 */
.game-controls-container {
margin-bottom: 15px;
}
.game-controls-container .game-controls-section {
display: flex;
flex-direction: column;
gap: 8px;
align-items: stretch;
}
.game-controls-container .btn {
width: 100%;
min-width: auto;
justify-content: center;
}
.game-score-bar {
display: flex;
gap: 15px;
justify-content: center;
align-items: center;
}
.game-controls-bar {
display: flex;
gap: 8px;
justify-content: center;
}
.score-box {
background: linear-gradient(135deg,
rgba(255, 255, 255, 0.25) 0%,
rgba(255, 255, 255, 0.15) 50%,
rgba(255, 255, 255, 0.1) 100%);
padding: 12px 18px;
border-radius: 15px;
text-align: center;
backdrop-filter: blur(20px);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 3px;
white-space: nowrap;
border: 2px solid rgba(255, 255, 255, 0.2);
box-shadow:
0 6px 24px rgba(0, 0, 0, 0.15),
inset 0 1px 0 rgba(255, 255, 255, 0.3),
0 0 0 1px rgba(255, 255, 255, 0.1);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
min-width: 100px;
}
.score-box::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg,
transparent,
rgba(255, 255, 255, 0.2),
transparent);
transition: left 0.6s ease;
}
.score-box:hover {
transform: translateY(-2px) scale(1.02);
box-shadow:
0 12px 40px rgba(0, 0, 0, 0.25),
inset 0 1px 0 rgba(255, 255, 255, 0.4),
0 0 0 1px rgba(255, 255, 255, 0.2);
border-color: rgba(255, 255, 255, 0.3);
}
.score-box:hover::before {
left: 100%;
}
.score-label {
font-size: 12px;
font-weight: 600;
color: rgba(255, 255, 255, 0.9);
margin: 0;
text-transform: uppercase;
letter-spacing: 1px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
.score-value {
font-size: 28px;
font-weight: 900;
margin: 0;
background: linear-gradient(135deg, #ffd700, #ffed4e, #ffd700);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
font-family: 'Arial Black', Arial, sans-serif;
line-height: 1;
}
/* 特殊效果:当分数更新时的动画 */
.score-value.score-updated {
animation: scoreUpdate 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
@keyframes scoreUpdate {
0% {
transform: scale(1);
}
50% {
transform: scale(1.2);
text-shadow: 0 0 20px rgba(255, 215, 0, 0.8);
}
100% {
transform: scale(1);
}
}
/* 最高分特殊样式 */
.score-box.best-score {
background: linear-gradient(135deg,
rgba(255, 107, 107, 0.3) 0%,
rgba(238, 90, 82, 0.2) 50%,
rgba(255, 107, 107, 0.15) 100%);
border-color: rgba(255, 107, 107, 0.3);
}
.score-box.best-score:hover {
border-color: rgba(255, 107, 107, 0.4);
box-shadow:
0 12px 40px rgba(255, 107, 107, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.4),
0 0 0 1px rgba(255, 107, 107, 0.2);
}
.score-box.best-score .score-value {
background: linear-gradient(135deg, #ff6b6b, #ee5a52, #ff6b6b);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.game-board {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(4, 1fr);
gap: 18px;
width: 100%;
max-width: 500px;
aspect-ratio: 1;
background: linear-gradient(145deg, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.2));
padding: 25px;
border-radius: 25px;
box-shadow:
inset 0 0 30px rgba(0, 0, 0, 0.4),
0 15px 35px rgba(0, 0, 0, 0.3),
0 5px 15px rgba(0, 0, 0, 0.2);
position: relative;
border: 2px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(15px);
}
.tile {
background: linear-gradient(135deg,
rgba(255, 255, 255, 0.2) 0%,
rgba(255, 255, 255, 0.1) 50%,
rgba(255, 255, 255, 0.05) 100%);
border-radius: 20px;
display: flex;
justify-content: center;
align-items: center;
font-size: 2em;
font-weight: 900;
font-family: 'Arial Black', Arial, sans-serif;
transition: all 0.3s ease;
border: 2px solid rgba(255, 255, 255, 0.2);
backdrop-filter: blur(15px);
box-shadow:
0 10px 30px rgba(0, 0, 0, 0.2),
inset 0 2px 0 rgba(255, 255, 255, 0.3),
inset 0 -2px 0 rgba(0, 0, 0, 0.1);
position: relative;
overflow: hidden;
}
.tile::before {
content: '';
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
background: linear-gradient(45deg,
rgba(255, 255, 255, 0.1),
transparent,
rgba(255, 255, 255, 0.1));
border-radius: 22px;
z-index: -1;
opacity: 0;
transition: opacity 0.3s ease;
}
.tile:hover::before {
opacity: 1;
}
.tile-2 {
background: linear-gradient(135deg,
#ffffff 0%,
#f8f5f0 50%,
#f0e8dc 100%);
color: #776e65;
border: 2px solid rgba(240, 232, 220, 0.6);
box-shadow:
0 8px 25px rgba(238, 228, 218, 0.3),
inset 0 2px 0 rgba(255, 255, 255, 0.8),
inset 0 -2px 0 rgba(240, 232, 220, 0.3);
text-shadow: 0 1px 2px rgba(255, 255, 255, 0.8);
}
.tile-4 {
background: linear-gradient(135deg,
#fdf6e3 0%,
#f4ead8 50%,
#ede0c8 100%);
color: #776e65;
border: 2px solid rgba(237, 224, 200, 0.6);
box-shadow:
0 8px 25px rgba(237, 224, 200, 0.3),
inset 0 2px 0 rgba(255, 255, 255, 0.7),
inset 0 -2px 0 rgba(237, 224, 200, 0.3);
text-shadow: 0 1px 2px rgba(255, 255, 255, 0.8);
}
.tile-8 {
background: linear-gradient(135deg,
#ffb347 0%,
#f7a02b 50%,
#f2941f 100%);
color: #ffffff;
border: 2px solid rgba(242, 148, 31, 0.6);
box-shadow:
0 10px 30px rgba(242, 177, 121, 0.4),
inset 0 2px 0 rgba(255, 255, 255, 0.6),
inset 0 -2px 0 rgba(242, 148, 31, 0.3);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
}
.tile-16 {
background: linear-gradient(135deg,
#ff8c42 0%,
#ff7b25 50%,
#ff6a08 100%);
color: #ffffff;
border: 2px solid rgba(255, 106, 8, 0.6);
box-shadow:
0 10px 30px rgba(245, 149, 99, 0.4),
inset 0 2px 0 rgba(255, 255, 255, 0.6),
inset 0 -2px 0 rgba(255, 106, 8, 0.3);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
}
.tile-32 {
background: linear-gradient(135deg,
#ff6b47 0%,
#ff5722 50%,
#e64a19 100%);
color: #ffffff;
border: 2px solid rgba(230, 74, 25, 0.6);
box-shadow:
0 10px 30px rgba(246, 124, 95, 0.4),
inset 0 2px 0 rgba(255, 255, 255, 0.6),
inset 0 -2px 0 rgba(230, 74, 25, 0.3);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
}
.tile-64 {
background: linear-gradient(135deg,
#ff5722 0%,
#f4511e 50%,
#d84315 100%);
color: #ffffff;
border: 2px solid rgba(216, 67, 21, 0.6);
box-shadow:
0 12px 35px rgba(246, 94, 59, 0.5),
inset 0 2px 0 rgba(255, 255, 255, 0.6),
inset 0 -2px 0 rgba(216, 67, 21, 0.3);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}
.tile-128 {
background: linear-gradient(135deg,
#ffd700 0%,
#ffed4e 50%,
#ffc107 100%);
color: #ffffff;
font-size: 1.8em;
border: 2px solid rgba(255, 193, 7, 0.6);
box-shadow:
0 12px 35px rgba(255, 215, 0, 0.4),
inset 0 2px 0 rgba(255, 255, 255, 0.6),
inset 0 -2px 0 rgba(255, 193, 7, 0.3);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
}
.tile-256 {
background: linear-gradient(135deg,
#ffb300 0%,
#ffa000 50%,
#ff8f00 100%);
color: #ffffff;
font-size: 1.8em;
border: 2px solid rgba(255, 143, 0, 0.6);
box-shadow:
0 12px 35px rgba(255, 160, 0, 0.4),
inset 0 2px 0 rgba(255, 255, 255, 0.6),
inset 0 -2px 0 rgba(255, 143, 0, 0.3);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
}
.tile-512 {
background: linear-gradient(135deg,
#ff9800 0%,
#ff8a65 50%,
#ff7043 100%);
color: #ffffff;
font-size: 1.8em;
border: 2px solid rgba(255, 112, 67, 0.6);
box-shadow:
0 12px 35px rgba(255, 138, 101, 0.4),
inset 0 2px 0 rgba(255, 255, 255, 0.6),
inset 0 -2px 0 rgba(255, 112, 67, 0.3);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
}
.tile-1024 {
background: linear-gradient(135deg,
#e91e63 0%,
#ad1457 50%,
#880e4f 100%);
color: #ffffff;
font-size: 1.5em;
border: 2px solid rgba(136, 14, 79, 0.6);
box-shadow:
0 15px 40px rgba(233, 30, 99, 0.5),
inset 0 2px 0 rgba(255, 255, 255, 0.6),
inset 0 -2px 0 rgba(136, 14, 79, 0.3);
text-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
}
.tile-2048 {
background: linear-gradient(135deg,
#9c27b0 0%,
#7b1fa2 50%,
#4a148c 100%);
color: #ffffff;
font-size: 1.5em;
border: 2px solid rgba(74, 20, 140, 0.6);
box-shadow:
0 15px 40px rgba(156, 39, 176, 0.6),
inset 0 2px 0 rgba(255, 255, 255, 0.6),
inset 0 -2px 0 rgba(74, 20, 140, 0.3);
text-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
}
.tile-4096 {
background: linear-gradient(135deg,
#673ab7 0%,
#512da8 50%,
#311b92 100%);
color: #ffffff;
font-size: 1.3em;
border: 2px solid rgba(49, 27, 146, 0.6);
box-shadow:
0 15px 40px rgba(103, 58, 183, 0.6),
inset 0 2px 0 rgba(255, 255, 255, 0.6),
inset 0 -2px 0 rgba(49, 27, 146, 0.3);
text-shadow: 0 2px 6px rgba(0, 0, 0, 0.7);
}
.tile-8192 {
background: linear-gradient(135deg,
#3f51b5 0%,
#303f9f 50%,
#1a237e 100%);
color: #ffffff;
font-size: 1.2em;
border: 2px solid rgba(26, 35, 126, 0.6);
box-shadow:
0 15px 40px rgba(63, 81, 181, 0.6),
inset 0 2px 0 rgba(255, 255, 255, 0.6),
inset 0 -2px 0 rgba(26, 35, 126, 0.3);
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.8);
}
/* 动画效果 */
@keyframes pulse {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
}
@keyframes pulse-glow {
0%,
100% {
transform: scale(1);
box-shadow:
0 12px 35px rgba(237, 207, 114, 0.6),
inset 0 1px 0 rgba(255, 255, 255, 0.5),
0 0 20px rgba(255, 215, 0, 0.3);
}
50% {
transform: scale(1.08);
box-shadow:
0 15px 45px rgba(255, 215, 0, 0.8),
inset 0 1px 0 rgba(255, 255, 255, 0.7),
0 0 30px rgba(255, 215, 0, 0.6);
}
}
@keyframes mega-glow {
0% {
transform: scale(1);
box-shadow:
0 15px 40px rgba(237, 197, 63, 0.7),
inset 0 1px 0 rgba(255, 255, 255, 0.6),
0 0 30px rgba(255, 215, 0, 0.6);
filter: brightness(1) saturate(1);
}
100% {
transform: scale(1.1);
box-shadow:
0 20px 60px rgba(255, 215, 0, 1),
inset 0 2px 0 rgba(255, 255, 255, 0.8),
0 0 50px rgba(255, 215, 0, 1);
filter: brightness(1.3) saturate(1.5);
}
}
@keyframes rainbow-supreme {
0% {
filter: hue-rotate(0deg) brightness(1.2) saturate(1.5);
box-shadow:
0 15px 40px rgba(60, 58, 50, 0.8),
inset 0 1px 0 rgba(255, 255, 255, 0.3),
0 0 40px rgba(255, 100, 255, 0.4);
}
25% {
filter: hue-rotate(90deg) brightness(1.4) saturate(1.8);
box-shadow:
0 18px 50px rgba(100, 255, 100, 0.6),
inset 0 1px 0 rgba(255, 255, 255, 0.4),
0 0 50px rgba(100, 255, 100, 0.6);
}
50% {
filter: hue-rotate(180deg) brightness(1.3) saturate(2);
box-shadow:
0 20px 60px rgba(100, 100, 255, 0.7),
inset 0 1px 0 rgba(255, 255, 255, 0.5),
0 0 60px rgba(100, 100, 255, 0.7);
}
75% {
filter: hue-rotate(270deg) brightness(1.5) saturate(1.6);
box-shadow:
0 18px 50px rgba(255, 255, 100, 0.8),
inset 0 1px 0 rgba(255, 255, 255, 0.4),
0 0 50px rgba(255, 255, 100, 0.8);
}
100% {
filter: hue-rotate(360deg) brightness(1.2) saturate(1.5);
box-shadow:
0 15px 40px rgba(255, 100, 255, 0.6),
inset 0 1px 0 rgba(255, 255, 255, 0.3),
0 0 40px rgba(255, 100, 255, 0.6);
}
}
@keyframes glow {
0% {
box-shadow: 0 8px 25px rgba(237, 194, 46, 0.7);
}
100% {
box-shadow: 0 12px 35px rgba(255, 215, 0, 0.9);
}
}
@keyframes rainbow {
0% {
filter: hue-rotate(0deg) brightness(1.2);
}
33% {
filter: hue-rotate(120deg) brightness(1.4);
}
66% {
filter: hue-rotate(240deg) brightness(1.3);
}
100% {
filter: hue-rotate(360deg) brightness(1.2);
}
}
.btn {
background: linear-gradient(45deg, #e74c3c, #c0392b);
color: white;
border: none;
padding: 10px 14px;
border-radius: 12px;
cursor: pointer;
font-size: 11px;
transition: all 0.3s ease;
box-shadow: 0 3px 8px rgba(231, 76, 60, 0.3);
font-weight: bold;
min-width: 70px;
white-space: nowrap;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}
.auto-button {
background: #27ae60;
}
.auto-button:hover {
background: #2ecc71;
}
.auto-button.active {
background: #e74c3c;
}
.auto-button.active:hover {
background: #c0392b;
}
.reset-button {
background: linear-gradient(45deg, #f39c12, #e67e22);
}
.reset-button:hover {
background: linear-gradient(45deg, #e67e22, #d35400);
}
.game-over {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.9);
display: none;
justify-content: center;
align-items: center;
flex-direction: column;
border-radius: 10px;
z-index: 100;
}
.game-over.show {
display: flex;
}
.game-over h2 {
color: #333;
margin-bottom: 20px;
font-size: 2em;
}
.main-container {
display: flex;
gap: 15px;
width: 100%;
align-items: stretch;
justify-content: center;
padding: 10px;
min-height: calc(100vh - 20px);
box-sizing: border-box;
}
.side-panel {
flex: 0 0 260px;
background: rgba(255, 255, 255, 0.1);
border-radius: 15px;
backdrop-filter: blur(10px);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
padding: 15px;
display: flex;
flex-direction: column;
height: 90vh;
max-height: 850px;
overflow: hidden;
box-sizing: border-box;
}
.side-panel h3 {
font-size: 1.1rem;
margin-bottom: 8px;
text-align: center;
background: linear-gradient(45deg, #ff6b6b, #feca57);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
padding-bottom: 6px;
border-bottom: 2px solid rgba(255, 255, 255, 0.2);
flex-shrink: 0;
}
.game-container {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
text-align: center;
padding: 20px 15px 15px 15px;
background: rgba(255, 255, 255, 0.1);
border-radius: 15px;
backdrop-filter: blur(10px);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
margin: 0;
max-width: none;
min-width: 0;
gap: 15px;
height: 90vh;
max-height: 850px;
}
.game-content-wrapper {
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
width: 100%;
gap: 30px;
padding: 20px 0;
}
/* 左右两侧得分框的特殊样式 */
.score-left,
.score-right {
flex: 0 0 auto;
align-self: center;
}
.score-left {
order: 1;
}
.game-board {
order: 2;
flex: 0 0 auto;
}
.score-right {
order: 3;
}
.guide-content {
display: flex;
flex-direction: column;
gap: 8px;
flex: 1;
overflow: hidden;
width: 100%;
box-sizing: border-box;
height: 100%;
justify-content: space-between;
}
.guide-section {
margin-bottom: 10px;
flex: 1;
}
.guide-section h4 {
font-size: 0.85rem;
margin-bottom: 6px;
color: #feca57;
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
padding-bottom: 3px;
}
.guide-item {
background: rgba(255, 255, 255, 0.1);
padding: 4px 6px;
border-radius: 4px;
font-size: 0.7rem;
line-height: 1.2;
border-left: 3px solid #feca57;
transition: all 0.3s ease;
margin-bottom: 3px;
}
.guide-item:hover {
background: rgba(255, 255, 255, 0.15);
transform: translateX(5px);
border-left-color: #ff6b6b;
}
.guide-item strong {
color: #feca57;
}
/* AI算法信息特殊样式 */
.ai-algorithm-item {
background: linear-gradient(135deg,
rgba(102, 126, 234, 0.15) 0%,
rgba(118, 75, 162, 0.1) 100%);
border: 1px solid rgba(102, 126, 234, 0.3);
border-radius: 10px;
padding: 10px;
margin: 6px 0;
overflow: hidden;
width: 100%;
box-sizing: border-box;
}
.algorithm-title {
font-size: 0.8rem;
font-weight: bold;
color: #feca57;
margin-bottom: 6px;
text-align: center;
border-bottom: 1px solid rgba(254, 202, 87, 0.3);
padding-bottom: 4px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.algorithm-details {
display: flex;
flex-direction: column;
gap: 3px;
width: 100%;
overflow: hidden;
}
.algorithm-feature {
font-size: 0.65rem;
line-height: 1.2;
color: rgba(255, 255, 255, 0.9);
padding: 2px 0;
border-left: 2px solid rgba(102, 126, 234, 0.4);
padding-left: 8px;
margin: 2px 0;
overflow: hidden;
word-wrap: break-word;
word-break: break-all;
white-space: normal;
}
.algorithm-feature strong {
color: #a8d8ff;
}
.auto-controls {
margin-top: 15px;
padding: 15px;
background: rgba(0, 0, 0, 0.05);
border-radius: 10px;
}
.speed-control {
margin: 10px 0;
}
.speed-control label {
display: inline-block;
width: 80px;
text-align: left;
color: white;
}
.speed-control input {
width: 150px;
}
.ai-info {
font-size: 11px;
color: #eee;
margin-top: 5px;
background: rgba(0, 0, 0, 0.1);
padding: 8px;
border-radius: 5px;
text-align: left;
line-height: 1.4;
}
/* 超级玩家模式区域整体样式 */
.settings-container {
display: flex;
flex-direction: column;
gap: 12px;
height: 100%;
}
.settings-section {
background: rgba(255, 255, 255, 0.08);
border-radius: 12px;
padding: 12px;
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
transition: all 0.3s ease;
}
.settings-section:hover {
background: rgba(255, 255, 255, 0.12);
border-color: rgba(255, 255, 255, 0.2);
}
.settings-section-title {
font-size: 0.9rem;
font-weight: bold;
margin-bottom: 8px;
color: #feca57;
display: flex;
align-items: center;
gap: 6px;
border-bottom: 1px solid rgba(254, 202, 87, 0.3);
padding-bottom: 6px;
white-space: nowrap;
}
.settings-description {
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.8);
line-height: 1.3;
margin-bottom: 10px;
}
/* 超级玩家模式样式 */
.super-player-container {
position: relative;
}
.super-player-container.active {
background: linear-gradient(135deg, rgba(255, 215, 0, 0.2), rgba(255, 193, 7, 0.1));
border-color: rgba(255, 215, 0, 0.4);
box-shadow: 0 0 25px rgba(255, 215, 0, 0.3);
}
.super-player-container.active::before {
content: '';
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
background: linear-gradient(45deg, #ffd700, #ffed4e, #ffd700);
border-radius: 14px;
z-index: -1;
opacity: 0.3;
}
.ai-game-toggle {
background: linear-gradient(135deg, #ffd700, #ffed4e);
color: #333;
border: none;
padding: 10px 16px;
border-radius: 20px;
cursor: pointer;
font-size: 12px;
font-weight: bold;
transition: all 0.3s ease;
width: 100%;
box-shadow: 0 3px 12px rgba(255, 215, 0, 0.3);
text-transform: uppercase;
letter-spacing: 0.3px;
}
.ai-game-toggle:hover {
transform: translateY(-1px);
box-shadow: 0 4px 15px rgba(255, 215, 0, 0.4);
}
.ai-game-toggle.active {
background: linear-gradient(135deg, #ff6b6b, #ee5a52);
color: white;
box-shadow: 0 3px 12px rgba(255, 107, 107, 0.4);
}
.ai-game-toggle.active:hover {
box-shadow: 0 4px 15px rgba(255, 107, 107, 0.5);
}
.ai-play-status {
margin-top: 8px;
padding: 6px 10px;
background: rgba(255, 215, 0, 0.2);
border-radius: 15px;
font-size: 10px;
color: #ffd700;
font-weight: bold;
text-align: center;
border: 1px solid rgba(255, 215, 0, 0.3);
}
/* AI高级设置样式 */
.ai-advanced-settings {
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.speed-control {
margin-bottom: 10px;
padding: 8px;
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.speed-control-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 6px;
}
.speed-control label {
font-size: 10px;
color: #feca57;
font-weight: bold;
}
.speed-control input[type="range"] {
width: 100%;
height: 4px;
border-radius: 2px;
background: rgba(255, 255, 255, 0.2);
outline: none;
-webkit-appearance: none;
}
.speed-control input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 14px;
height: 14px;
border-radius: 50%;
background: linear-gradient(135deg, #ffd700, #ffed4e);
cursor: pointer;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}
.speed-value {
font-size: 9px;
color: #fff;
background: rgba(255, 215, 0, 0.2);
padding: 1px 6px;
border-radius: 8px;
border: 1px solid rgba(255, 215, 0, 0.3);
}
.ai-info {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2));
border: 1px solid rgba(102, 126, 234, 0.3);
border-radius: 8px;
padding: 10px;
font-size: 9px;
color: #e8e8e8;
line-height: 1.4;
}
.ai-info strong {
color: #feca57;
font-size: 10px;
display: block;
margin-bottom: 6px;
}
.ai-info-item {
margin: 2px 0;
padding-left: 6px;
border-left: 2px solid rgba(254, 202, 87, 0.3);
}
/* 功能说明区域样式 */
.feature-list {
display: flex;
flex-direction: column;
gap: 4px;
}
.feature-item {
background: rgba(255, 255, 255, 0.05);
padding: 6px 8px;
border-radius: 6px;
border-left: 2px solid #feca57;
font-size: 0.7rem;
line-height: 1.2;
transition: all 0.3s ease;
}
.feature-item:hover {
background: rgba(255, 255, 255, 0.1);
transform: translateX(2px);
border-left-color: #ff6b6b;
}
.feature-item strong {
color: #feca57;
}
/* 验证模态框样式 */
.question-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
display: none;
align-items: center;
justify-content: center;
z-index: 1001;
}
.question-content {
background: linear-gradient(135deg, #667eea, #764ba2);
padding: 40px;
border-radius: 15px;
text-align: center;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
color: white;
max-width: 400px;
width: 90%;
}
.question-input {
width: 100%;
padding: 12px;
margin: 15px 0;
border: none;
border-radius: 8px;
font-size: 16px;
text-align: center;
background: rgba(255, 255, 255, 0.9);
box-sizing: border-box;
}
.question-buttons {
display: flex;
gap: 10px;
justify-content: center;
margin-top: 20px;
}
@media (max-width: 1200px) {
.main-container {
flex-direction: column;
align-items: center;
gap: 15px;
}
.side-panel {
flex: none;
width: 100%;
max-width: 600px;
}
.game-container {
margin: 0;
width: 100%;
max-width: 600px;
}
}
@media (max-width: 768px) {
body {
padding: 15px;
}
.main-container {
padding: 0 15px 20px 15px;
gap: 15px;
max-width: none;
}
.game-title h1 {
font-size: 2.5em;
}
.side-panel {
padding: 18px;
}
.game-container {
padding: 15px;
min-width: auto;
}
.game-board {
width: 100%;
max-width: 350px;
}
.game-tile {
font-size: 1.8em;
min-height: 70px;
min-width: 70px;
}
.game-tile.tile-128,
.game-tile.tile-256,
.game-tile.tile-512 {
font-size: 1.5em;
}
.game-tile.tile-1024,
.game-tile.tile-2048 {
font-size: 1.2em;
}
.game-header-bar {
flex-direction: column;
gap: 15px;
}
.game-info {
justify-content: center;
flex-wrap: wrap;
}
.game-controls-bar {
justify-content: center;
flex-wrap: wrap;
}
.score-box {
padding: 8px 16px;
min-width: 120px;
}
.score-label {
font-size: 14px;
}
.score-value {
font-size: 20px;
}
.btn {
font-size: 12px;
padding: 8px 16px;
min-width: 80px;
}
.guide-item {
font-size: 0.8rem;
}
}
</style>
</head>
<body>
<!-- 备用背景 -->
<div class="fallback-background"></div>
<!-- 真实视频背景 -->
<div class="video-background">
<video autoplay muted loop playsinline id="bgVideo" preload="auto">
<source src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
type="video/mp4">
<source src="https://www.w3schools.com/html/mov_bbb.mp4" type="video/mp4">
<source src="https://sample-videos.com/zip/10/mp4/SampleVideo_1280x720_5mb.mp4" type="video/mp4">
您的浏览器不支持HTML5视频播放。
</video>
</div>
<!-- 视频遮罩层 -->
<div class="video-overlay"></div>
<!-- 主要内容区域:左中右三栏布局 -->
<div class="main-container">
<!-- 左侧操作指南面板 -->
<div class="side-panel">
<h3>📖 游戏指南</h3>
<div class="guide-content">
<div class="guide-section">
<h4>⌨️ 基础操作</h4>
<div class="guide-item">• <strong>方向键</strong> 或 <strong>WASD</strong>:移动合并方块</div>
<div class="guide-item">• <strong>触摸滑动</strong>:移动端手势操作</div>
<div class="guide-item">• <strong>空格键</strong>:暂停/继续游戏</div>
</div>
<div class="guide-section">
<h4>🎯 游戏规则</h4>
<div class="guide-item">• 滑动方块,相同数字会<strong>自动合并</strong></div>
<div class="guide-item">• 目标是创造出 <strong>2048</strong> 方块</div>
<div class="guide-item">• 方块填满且无法移动时游戏结束</div>
<div class="guide-item">• 达到2048后可继续挑战更高数字</div>
</div>
<div class="guide-section">
<h4>🧠 高级策略</h4>
<div class="guide-item">• <strong>角落策略</strong>:将最大数字保持在角落</div>
<div class="guide-item">• <strong>单向原则</strong>:尽量保持一个主要移动方向</div>
<div class="guide-item">• <strong>空间管理</strong>:避免随意填满空格</div>
<div class="guide-item">• <strong>提前规划</strong>:思考每步移动的后果</div>
</div>
<div class="guide-section">
<h4>🤖 终极AI算法</h4>
<div class="guide-item ai-algorithm-item">
<div class="algorithm-title">🧠 <strong>Expectimax v3.0</strong></div>
<div class="algorithm-details">
<div class="algorithm-feature">🔍 <strong>搜索算法:</strong> Expectimax深度搜索</div>
<div class="algorithm-feature">🎯 <strong>核心策略:</strong> 角落蛇形权重矩阵</div>
<div class="algorithm-feature">⚡ <strong>启发函数:</strong> 单调性 + 平滑度 + 空格奖励</div>
<div class="algorithm-feature">📊 <strong>成功率:</strong> 99%+ 达到2048, 90%+ 达到4096</div>
<div class="algorithm-feature">✨ <strong>特色:</strong> 自适应搜索深度,智能决策</div>
</div>
</div>
</div>
</div>
</div>
<!-- 中间游戏区域 -->
<div class="game-container">
<!-- 标题区域 - 独占一行居中 -->
<div class="game-title">
<h1>2048 - 终极版</h1>
</div>
<!-- 游戏内容区域 - 棋盘居中,得分左右分布 -->
<div class="game-content-wrapper">
<!-- 左侧得分 -->
<div class="score-box score-left">
<div class="score-label">当前得分</div>
<div class="score-value" id="score">0</div>
</div>
<!-- 中间棋盘 -->
<div class="game-board" id="game-board">
<div class="game-over" id="game-over">
<h2 id="game-over-text">游戏结束!</h2>
<button class="btn" onclick="game.restart()">重新开始</button>
</div>
</div>
<!-- 右侧最高分 -->
<div class="score-box best-score score-right">
<div class="score-label">历史最高</div>
<div class="score-value" id="best-score">0</div>
</div>
</div>
</div>
<!-- 右侧面板:超级玩家模式 -->
<div class="side-panel">
<h3>🌟 超级玩家模式</h3>
<div class="settings-container">
<!-- 游戏控制区域 -->
<div class="settings-section game-controls-container">
<div class="settings-section-title">
🎮 游戏控制
</div>
<div class="settings-description">
基础游戏操作和功能控制
</div>
<div class="game-controls-section">
<button class="btn" onclick="game.restart()">新游戏</button>
<button class="btn" onclick="game.undo()" id="undo-btn">撤销</button>
<button class="btn reset-button" onclick="game.resetGameRecord()" id="reset-btn">重置记录</button>
</div>
</div>
<!-- 超级玩家模式区域 -->
<div class="settings-section super-player-container" id="aiGameMode">
<div class="settings-section-title">
🌟 超级玩家模式
</div>
<div class="settings-description">
通过验证解锁AI自动游戏功能,体验终极算法的强大实力
</div>
<button class="ai-game-toggle" onclick="handleAIGameToggle()" id="aiGameToggle">
开启超级玩家模式
</button>
<div class="ai-play-status" id="aiPlayStatus" style="display: none;">
🤖 AI正在游戏中...
</div>
<!-- AI高级设置区域 - 只有验证通过后才显示 -->
<div class="ai-advanced-settings" id="aiAdvancedSettings" style="display: none;">
<div class="speed-control">
<div class="speed-control-header">
<label for="speed-slider">⚡ AI速度控制</label>
<span class="speed-value" id="speed-value">150ms</span>
</div>
<input type="range" id="speed-slider" min="50" max="500" value="150"
onchange="game.setAutoSpeed(this.value)">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 超级玩家模式验证模态框 -->
<div class="question-modal" id="questionModal">
<div class="question-content">
<h2>🔐 超级玩家验证</h2>
<p id="questionText">请回答以下问题以开启超级玩家模式:</p>
<input type="text" class="question-input" id="questionInput" placeholder="请输入答案..." maxlength="50">
<div class="question-buttons">
<button class="btn" onclick="submitAnswer()">确认</button>
<button class="btn" onclick="closeQuestionModal()">取消</button>
</div>
</div>
</div>
<script>
// AI游戏验证问题集合
const aiGameQuestions = [
{
question: "最靓仔的是谁?",
answer: "你"
},
{
question: "1+1等于几?",
answer: "2"
},
{
question: "2048游戏的目标数字是多少?",
answer: "2048"
},
{
question: "一周有几天?",
answer: "7"
}
];
let currentQuestion = null;
let superPlayerMode = false; // 超级玩家模式状态
// 超级玩家模式验证相关函数
function handleAIGameToggle() {
if (!superPlayerMode) {
// 超级玩家模式未开启,需要验证
showQuestionModal();
} else {
// 超级玩家模式已开启,可以控制AI游戏
if (game.isAutoPlaying) {
// 停止AI游戏
game.stopAutoPlay();
} else {
// 开始AI游戏
game.startAutoPlay();
}
updateAIGameUI();
}
}
function showQuestionModal() {
currentQuestion = aiGameQuestions[Math.floor(Math.random() * aiGameQuestions.length)];
document.getElementById('questionText').textContent = `请回答以下问题以开启超级玩家模式:${currentQuestion.question}`;
document.getElementById('questionInput').value = '';
document.getElementById('questionModal').style.display = 'flex';
document.getElementById('questionInput').focus();
}
function closeQuestionModal() {
document.getElementById('questionModal').style.display = 'none';
currentQuestion = null;
}
function submitAnswer() {
const userAnswer = document.getElementById('questionInput').value.trim();
const correctAnswer = currentQuestion.answer;
if (userAnswer === correctAnswer) {
// 答案正确,开启超级玩家模式
superPlayerMode = true;
updateAIGameUI();
closeQuestionModal();
// 显示成功消息
setTimeout(() => {
alert('🎉 验证成功!超级玩家模式已开启!\n现在可以使用AI游戏功能。');
}, 100);
} else {
// 答案错误
alert('❌ 答案错误,请重试!');
document.getElementById('questionInput').value = '';
document.getElementById('questionInput').focus();
}
}
function updateAIGameUI() {
const containerElement = document.getElementById('aiGameMode');
const toggleButton = document.getElementById('aiGameToggle');
const statusElement = document.getElementById('aiPlayStatus');
const advancedSettings = document.getElementById('aiAdvancedSettings');
if (superPlayerMode) {
containerElement.classList.add('active');
advancedSettings.style.display = 'block'; // 显示AI高级设置
if (game.isAutoPlaying) {
toggleButton.textContent = '停止AI游戏';
toggleButton.classList.add('active');
statusElement.style.display = 'block';
} else {
toggleButton.textContent = '开始AI游戏';
toggleButton.classList.remove('active');
statusElement.style.display = 'none';
}
} else {
containerElement.classList.remove('active');
toggleButton.textContent = '开启超级玩家模式';
toggleButton.classList.remove('active');
statusElement.style.display = 'none';
advancedSettings.style.display = 'none'; // 隐藏AI高级设置
}
}
// 键盘事件监听(回车键提交答案)
document.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && document.getElementById('questionModal').style.display === 'flex') {
e.preventDefault();
submitAnswer();
}
});
class Game2048 {
constructor() {
this.board = [];
this.score = 0;
this.bestScore = localStorage.getItem('bestScore') || 0;
this.previousState = null;
this.size = 4;
this.isAutoPlaying = false;
this.autoSpeed = 150;
this.autoInterval = null;
this.init();
}
init() {
this.board = Array(this.size).fill().map(() => Array(this.size).fill(0));
this.score = 0;
this.updateDisplay();
this.addRandomTile();
this.addRandomTile();
this.render();
this.bindEvents();
}
bindEvents() {
document.addEventListener('keydown', (e) => {
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
e.preventDefault();
if (!this.isAutoPlaying) {
this.saveState();
this.move(e.key);
}
}
});
// 触摸事件支持
let startX, startY;
const gameBoard = document.getElementById('game-board');
gameBoard.addEventListener('touchstart', (e) => {
if (!this.isAutoPlaying) {
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
}
});
gameBoard.addEventListener('touchend', (e) => {
if (!startX || !startY || this.isAutoPlaying) return;
const endX = e.changedTouches[0].clientX;
const endY = e.changedTouches[0].clientY;
const diffX = startX - endX;
const diffY = startY - endY;
if (Math.abs(diffX) > Math.abs(diffY)) {
if (diffX > 0) {
this.saveState();
this.move('ArrowLeft');
} else {
this.saveState();
this.move('ArrowRight');
}
} else {
if (diffY > 0) {
this.saveState();
this.move('ArrowUp');
} else {
this.saveState();
this.move('ArrowDown');
}
}
startX = null;
startY = null;
});
}
saveState() {
this.previousState = {
board: this.board.map(row => [...row]),
score: this.score
};
}
undo() {
if (this.previousState && !this.isAutoPlaying) {
this.board = this.previousState.board;
this.score = this.previousState.score;
this.previousState = null;
this.render();
this.updateDisplay();
}
}
addRandomTile() {
const emptyCells = [];
for (let i = 0; i < this.size; i++) {
for (let j = 0; j < this.size; j++) {
if (this.board[i][j] === 0) {
emptyCells.push({ x: i, y: j });
}
}
}
if (emptyCells.length > 0) {
const randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
this.board[randomCell.x][randomCell.y] = Math.random() < 0.9 ? 2 : 4;
}
}
move(direction) {
let moved = false;
const newBoard = this.board.map(row => [...row]);
const originalScore = this.score;
switch (direction) {
case 'ArrowLeft':
moved = this.moveLeft(newBoard);
break;
case 'ArrowRight':
moved = this.moveRight(newBoard);
break;
case 'ArrowUp':
moved = this.moveUp(newBoard);
break;
case 'ArrowDown':
moved = this.moveDown(newBoard);
break;
}
if (moved) {
this.board = newBoard;
this.addRandomTile();
this.render();
this.updateDisplay();
const maxTile = Math.max(...this.board.flat());
if (maxTile >= 2048 && !this.hasShownWin) {
this.hasShownWin = true;
setTimeout(() => {
alert(`恭喜!达到了${maxTile}!游戏继续...`);
}, 100);
}
if (this.isGameOver()) {
this.showGameOver();
}
}
return moved;
}
moveLeft(board) {
let moved = false;
for (let i = 0; i < this.size; i++) {
const row = board[i].filter(val => val !== 0);
for (let j = 0; j < row.length - 1; j++) {
if (row[j] === row[j + 1]) {
row[j] *= 2;
this.score += row[j];
row.splice(j + 1, 1);
}
}
while (row.length < this.size) {
row.push(0);
}
for (let j = 0; j < this.size; j++) {
if (board[i][j] !== row[j]) {
moved = true;
}
board[i][j] = row[j];
}
}
return moved;
}
moveRight(board) {
let moved = false;
for (let i = 0; i < this.size; i++) {
const row = board[i].filter(val => val !== 0);
for (let j = row.length - 1; j > 0; j--) {
if (row[j] === row[j - 1]) {
row[j] *= 2;
this.score += row[j];
row.splice(j - 1, 1);
j--;
}
}
while (row.length < this.size) {
row.unshift(0);
}
for (let j = 0; j < this.size; j++) {
if (board[i][j] !== row[j]) {
moved = true;
}
board[i][j] = row[j];
}
}
return moved;
}
moveUp(board) {
let moved = false;
for (let j = 0; j < this.size; j++) {
const column = [];
for (let i = 0; i < this.size; i++) {
if (board[i][j] !== 0) {
column.push(board[i][j]);
}
}
for (let i = 0; i < column.length - 1; i++) {
if (column[i] === column[i + 1]) {
column[i] *= 2;
this.score += column[i];
column.splice(i + 1, 1);
}
}
while (column.length < this.size) {
column.push(0);
}
for (let i = 0; i < this.size; i++) {
if (board[i][j] !== column[i]) {
moved = true;
}
board[i][j] = column[i];
}
}
return moved;
}
moveDown(board) {
let moved = false;
for (let j = 0; j < this.size; j++) {
const column = [];
for (let i = 0; i < this.size; i++) {
if (board[i][j] !== 0) {
column.push(board[i][j]);
}
}
for (let i = column.length - 1; i > 0; i--) {
if (column[i] === column[i - 1]) {
column[i] *= 2;
this.score += column[i];
column.splice(i - 1, 1);
i--;
}
}
while (column.length < this.size) {
column.unshift(0);
}
for (let i = 0; i < this.size; i++) {
if (board[i][j] !== column[i]) {
moved = true;
}
board[i][j] = column[i];
}
}
return moved;
}
render() {
const gameBoard = document.getElementById('game-board');
const tiles = gameBoard.querySelectorAll('.tile');
// 移除现有的数字方块
tiles.forEach(tile => {
if (!tile.classList.contains('game-over')) {
tile.remove();
}
});
// 创建新的方块
for (let i = 0; i < this.size; i++) {
for (let j = 0; j < this.size; j++) {
const tile = document.createElement('div');
tile.className = 'tile';
if (this.board[i][j] !== 0) {
tile.classList.add(`tile-${this.board[i][j]}`);
tile.textContent = this.board[i][j];
}
gameBoard.appendChild(tile);
}
}
}
updateDisplay() {
document.getElementById('score').textContent = this.score;
if (this.score > this.bestScore) {
this.bestScore = this.score;
localStorage.setItem('bestScore', this.bestScore);
}
document.getElementById('best-score').textContent = this.bestScore;
}
isGameOver() {
for (let i = 0; i < this.size; i++) {
for (let j = 0; j < this.size; j++) {
if (this.board[i][j] === 0) return false;
if (i < this.size - 1 && this.board[i + 1][j] === this.board[i][j]) return false;
if (j < this.size - 1 && this.board[i][j + 1] === this.board[i][j]) return false;
}
}
return true;
}
showGameOver() {
const gameOverElement = document.getElementById('game-over');
const gameOverText = document.getElementById('game-over-text');
const maxTile = Math.max(...this.board.flat());
gameOverText.textContent = `游戏结束!最高数字: ${maxTile}`;
gameOverElement.classList.add('show');
if (this.isAutoPlaying) this.stopAutoPlay();
}
restart() {
document.getElementById('game-over').classList.remove('show');
this.stopAutoPlay();
this.hasShownWin = false;
this.init();
}
resetGameRecord() {
// 确认对话框
if (confirm('确定要重置游戏记录吗?这将清除最高分记录,此操作不可撤销!')) {
// 清除本地存储的最高分
localStorage.removeItem('bestScore');
this.bestScore = 0;
// 停止自动游戏
this.stopAutoPlay();
// 重新初始化游戏
this.hasShownWin = false;
this.init();
// 显示重置成功提示
setTimeout(() => {
alert('游戏记录已重置!最高分已清零。');
}, 100);
}
}
toggleAutoPlay() {
if (this.isAutoPlaying) {
this.stopAutoPlay();
} else {
this.startAutoPlay();
}
}
startAutoPlay() {
this.isAutoPlaying = true;
updateAIGameUI();
this.autoInterval = setInterval(() => {
if (this.isGameOver()) {
this.stopAutoPlay();
this.showGameOver();
return;
}
const bestMove = this.getBestMove();
if (bestMove) {
this.saveState();
this.move(bestMove);
} else {
this.stopAutoPlay();
}
}, this.autoSpeed);
}
stopAutoPlay() {
this.isAutoPlaying = false;
if (this.autoInterval) {
clearInterval(this.autoInterval);
this.autoInterval = null;
}
updateAIGameUI();
}
setAutoSpeed(speed) {
this.autoSpeed = parseInt(speed);
document.getElementById('speed-value').textContent = speed + 'ms';
if (this.isAutoPlaying) {
this.stopAutoPlay();
this.startAutoPlay();
}
}
// ===== 终极AI算法 v3.0 =====
getBestMove() {
const moves = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
let bestMove = null;
let bestScore = -Infinity;
const searchDepth = this.getAdaptiveDepth();
for (const move of moves) {
const tempBoard = this.cloneBoard(this.board);
const tempScore = this.score;
if (this.simulateMove(tempBoard, move)) {
const score = this.expectimax(tempBoard, searchDepth, false);
if (score > bestScore) {
bestScore = score;
bestMove = move;
}
}
this.score = tempScore; // Restore score after simulation
}
return bestMove;
}
getAdaptiveDepth() {
const emptyCells = this.countEmptyCells(this.board);
if (emptyCells > 6) return 3;
if (emptyCells > 3) return 4;
return 5; // Deeper search when board is crowded
}
expectimax(board, depth, isPlayerTurn) {
if (depth === 0 || this.isTerminal(board)) {
return this.evaluate(board);
}
if (isPlayerTurn) { // Maximize for player
let maxScore = -Infinity;
const moves = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
for (const move of moves) {
const newBoard = this.cloneBoard(board);
const tempScore = this.score;
if (this.simulateMove(newBoard, move)) {
maxScore = Math.max(maxScore, this.expectimax(newBoard, depth - 1, false));
}
this.score = tempScore;
}
return maxScore;
} else { // Calculate expectation for computer
let totalScore = 0;
const emptyCells = this.getEmptyCells(board);
if (emptyCells.length === 0) {
return this.evaluate(board);
}
for (const cell of emptyCells) {
// 90% chance for a 2
const board2 = this.cloneBoard(board);
board2[cell.x][cell.y] = 2;
totalScore += 0.9 * this.expectimax(board2, depth - 1, true);
// 10% chance for a 4
const board4 = this.cloneBoard(board);
board4[cell.x][cell.y] = 4;
totalScore += 0.1 * this.expectimax(board4, depth - 1, true);
}
return totalScore / emptyCells.length;
}
}
isTerminal(board) {
for (let i = 0; i < this.size; i++) {
for (let j = 0; j < this.size; j++) {
if (board[i][j] === 0) return false;
if (i < this.size - 1 && board[i + 1][j] === board[i][j]) return false;
if (j < this.size - 1 && board[i][j + 1] === board[i][j]) return false;
}
}
return true;
}
evaluate(board) {
const emptyCellsWeight = 100000;
const monotonicityWeight = 10000;
const smoothnessWeight = 500;
const snakeWeight = 50000;
return this.getSnakePatternScore(board) * snakeWeight +
this.getMonotonicity(board) * monotonicityWeight +
this.getSmoothness(board) * smoothnessWeight +
this.countEmptyCells(board) * emptyCellsWeight;
}
getSnakePatternScore(board) {
const weights = [
[10, 8, 7, 6.5],
[5, 3, 1.5, 1],
[-0.5, -1.5, -1.8, -2],
[-3, -3.5, -4, -4.5]
];
let score = 0;
for (let i = 0; i < this.size; i++) {
for (let j = 0; j < this.size; j++) {
if (board[i][j] !== 0) {
score += board[i][j] * weights[i][j];
}
}
}
return score;
}
getMonotonicity(board) {
let totals = [0, 0, 0, 0]; // up, down, left, right
// left/right
for (let i = 0; i < this.size; i++) {
let current = 0;
let next = current + 1;
while (next < 4) {
while (next < 4 && board[i][next] === 0) next++;
if (next >= 4) next--;
const currentValue = board[i][current] !== 0 ? Math.log2(board[i][current]) : 0;
const nextValue = board[i][next] !== 0 ? Math.log2(board[i][next]) : 0;
if (currentValue > nextValue) {
totals[0] += nextValue - currentValue;
} else if (nextValue > currentValue) {
totals[1] += currentValue - nextValue;
}
current = next;
next++;
}
}
// up/down
for (let j = 0; j < this.size; j++) {
let current = 0;
let next = current + 1;
while (next < 4) {
while (next < 4 && board[next][j] === 0) next++;
if (next >= 4) next--;
const currentValue = board[current][j] !== 0 ? Math.log2(board[current][j]) : 0;
const nextValue = board[next][j] !== 0 ? Math.log2(board[next][j]) : 0;
if (currentValue > nextValue) {
totals[2] += nextValue - currentValue;
} else if (nextValue > currentValue) {
totals[3] += currentValue - nextValue;
}
current = next;
next++;
}
}
return Math.max(totals[0], totals[1]) + Math.max(totals[2], totals[3]);
}
getSmoothness(board) {
let smoothness = 0;
for (let i = 0; i < this.size; i++) {
for (let j = 0; j < this.size; j++) {
if (board[i][j] !== 0) {
const value = Math.log2(board[i][j]);
// right
if (j < this.size - 1 && board[i][j + 1] !== 0) {
const rightValue = Math.log2(board[i][j + 1]);
smoothness -= Math.abs(value - rightValue);
}
// down
if (i < this.size - 1 && board[i + 1][j] !== 0) {
const downValue = Math.log2(board[i + 1][j]);
smoothness -= Math.abs(value - downValue);
}
}
}
}
return smoothness;
}
// Helper functions
cloneBoard(board) {
return board.map(row => [...row]);
}
simulateMove(board, direction) {
const originalScore = this.score;
let moved = false;
switch (direction) {
case 'ArrowLeft': moved = this.moveLeft(board); break;
case 'ArrowRight': moved = this.moveRight(board); break;
case 'ArrowUp': moved = this.moveUp(board); break;
case 'ArrowDown': moved = this.moveDown(board); break;
}
this.score = originalScore;
return moved;
}
getEmptyCells(board) {
const emptyCells = [];
for (let i = 0; i < this.size; i++) {
for (let j = 0; j < this.size; j++) {
if (board[i][j] === 0) {
emptyCells.push({ x: i, y: j });
}
}
}
return emptyCells;
}
countEmptyCells(board) {
return this.getEmptyCells(board).length;
}
}
const game = new Game2048();
</script>
</body>
</html>
4.总结
这个游戏比较有意思的地方是利用算法去做事情,在这个过程中有算法无法突破512的方块合并的问题、权重比不够、游戏方法理解不足等问题,最后还是解决了,总结下来用算法练练手,后续在实战项目可以更加丝滑的使用算法解决问题