|
|
|
@ -1,518 +0,0 @@
|
|
|
|
|
import copy
|
|
|
|
|
import pygame
|
|
|
|
|
import random
|
|
|
|
|
from map_config import *
|
|
|
|
|
from button_event import *
|
|
|
|
|
import sound
|
|
|
|
|
|
|
|
|
|
ai_delay_time = 500
|
|
|
|
|
|
|
|
|
|
sound_flag = 0
|
|
|
|
|
#是否决定播放成功或者失败的音乐的标志位
|
|
|
|
|
|
|
|
|
|
#游戏得分
|
|
|
|
|
game_point = 0
|
|
|
|
|
game_step = 0
|
|
|
|
|
isAdd = 0
|
|
|
|
|
|
|
|
|
|
#板的二维数组
|
|
|
|
|
board = [
|
|
|
|
|
[0,2,4,8],
|
|
|
|
|
[0,16,32,64],
|
|
|
|
|
[128,0,256,0],
|
|
|
|
|
[512,1024,0,2048]
|
|
|
|
|
]
|
|
|
|
|
#返还row行 col列
|
|
|
|
|
#board[a,b] 对应的是a行b列
|
|
|
|
|
#比如说
|
|
|
|
|
#board = [[0,2,4,8],
|
|
|
|
|
# [0,16,32,64],
|
|
|
|
|
# [128,0,256,0],
|
|
|
|
|
# [512,1024,0,2048]]
|
|
|
|
|
# board[0][1] = 2
|
|
|
|
|
|
|
|
|
|
#板的栈 用于记录过去的值
|
|
|
|
|
board_stack = []
|
|
|
|
|
#得分的栈 用于记录过去的得分
|
|
|
|
|
point_stack = []
|
|
|
|
|
#步数的栈 用于记录过去的行动 0空 1上 2下 3左 4右
|
|
|
|
|
step_stack = []
|
|
|
|
|
#(之前弄过一个MyStack 因为有BUG只能放弃了)
|
|
|
|
|
|
|
|
|
|
#获取得分
|
|
|
|
|
def get_point():
|
|
|
|
|
return game_point
|
|
|
|
|
#获取步数
|
|
|
|
|
def get_step():
|
|
|
|
|
return game_step
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#获取任意行列的方块的值
|
|
|
|
|
def get_block_num(row,col):
|
|
|
|
|
return board[row][col]
|
|
|
|
|
|
|
|
|
|
#清除地图
|
|
|
|
|
def clear_map():
|
|
|
|
|
for row in range(0,4):
|
|
|
|
|
for col in range(0,4):
|
|
|
|
|
board[row][col] = 0
|
|
|
|
|
|
|
|
|
|
#判断里面有没有空的格子
|
|
|
|
|
def isEmpty():
|
|
|
|
|
for row in range(0,4):
|
|
|
|
|
for col in range(0,4):
|
|
|
|
|
if(board[row][col] == 0):
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
#给数组添加新的块
|
|
|
|
|
#有四分之一的可能产生4
|
|
|
|
|
#四分之三的可能产生2
|
|
|
|
|
def create_new_block():
|
|
|
|
|
rows = random.randint(0,3)
|
|
|
|
|
cols = random.randint(0,3)#生成行和列
|
|
|
|
|
|
|
|
|
|
while(board[rows][cols]!=0):
|
|
|
|
|
rows = random.randint(0,3)
|
|
|
|
|
cols = random.randint(0,3)#如果这个位置不是空的,就继续生成行和列
|
|
|
|
|
#print(rows,cols,board[rows][cols])
|
|
|
|
|
value = random.randint(0,3)
|
|
|
|
|
if(value == 3):
|
|
|
|
|
board[rows][cols] = 4
|
|
|
|
|
else:
|
|
|
|
|
board[rows][cols] = 2
|
|
|
|
|
|
|
|
|
|
#向上移动
|
|
|
|
|
def move_up():
|
|
|
|
|
global game_point
|
|
|
|
|
global game_step
|
|
|
|
|
global isAdd
|
|
|
|
|
isAdd = 0
|
|
|
|
|
for col in range(0,4):
|
|
|
|
|
index = 0
|
|
|
|
|
for row in range(1,4):
|
|
|
|
|
if board[row][col] > 0:
|
|
|
|
|
#当前数字等于上边数字
|
|
|
|
|
#上边数字 = 上边数字 + 当前数字, 当前数字 = 0
|
|
|
|
|
if board[row][col] == board[index][col]:
|
|
|
|
|
board[index][col] = board[row][col] + board[index][col]
|
|
|
|
|
game_point += board[row][col]
|
|
|
|
|
board[row][col] = 0
|
|
|
|
|
isAdd = 1
|
|
|
|
|
index += 1
|
|
|
|
|
#当前数字不等于上边数字
|
|
|
|
|
elif board[index][col] == 0:#当前数字上边有0
|
|
|
|
|
#上边数字 = 当前数字, 当前数字 = 0
|
|
|
|
|
board[index][col] = board[row][col]
|
|
|
|
|
board[row][col] = 0
|
|
|
|
|
isAdd = 1
|
|
|
|
|
else:
|
|
|
|
|
index += 1
|
|
|
|
|
#去除漏网之鱼
|
|
|
|
|
#index相当于慢指针,j相当于快指针
|
|
|
|
|
#也就是说快指针和慢指针中间可能存在一个以上的空格,或者index和j并未相邻
|
|
|
|
|
#上边数字 = 0
|
|
|
|
|
#数值: 上边数字 = 当前数字, 当前数字 = 0
|
|
|
|
|
if board[index][col] == 0:
|
|
|
|
|
board[index][col] = board[row][col]
|
|
|
|
|
board[row][col] = 0
|
|
|
|
|
isAdd = 1
|
|
|
|
|
game_step += isAdd
|
|
|
|
|
return isAdd
|
|
|
|
|
|
|
|
|
|
#向下移动
|
|
|
|
|
def move_down():
|
|
|
|
|
global game_point
|
|
|
|
|
global game_step
|
|
|
|
|
global isAdd
|
|
|
|
|
isAdd = 0
|
|
|
|
|
for col in range(0,4):
|
|
|
|
|
index = 3
|
|
|
|
|
for row in range(2,-1,-1):
|
|
|
|
|
if board[row][col] > 0:
|
|
|
|
|
#当前数字等于下边数字
|
|
|
|
|
#下边数字 = 下边数字 + 当前数字, 当前数字 = 0
|
|
|
|
|
if board[row][col] == board[index][col]:
|
|
|
|
|
board[index][col] = board[row][col] + board[index][col]
|
|
|
|
|
game_point += board[row][col]
|
|
|
|
|
board[row][col] = 0
|
|
|
|
|
index -= 1
|
|
|
|
|
isAdd = 1
|
|
|
|
|
#当前数字不等于下边数字
|
|
|
|
|
elif board[index][col] == 0:#当前数字上边有0
|
|
|
|
|
#下边数字 = 当前数字, 当前数字 = 0
|
|
|
|
|
board[index][col] = board[row][col]
|
|
|
|
|
board[row][col] = 0
|
|
|
|
|
isAdd = 1
|
|
|
|
|
else:
|
|
|
|
|
index -= 1
|
|
|
|
|
#去除漏网之鱼
|
|
|
|
|
#index相当于慢指针,j相当于快指针
|
|
|
|
|
#也就是说快指针和慢指针中间可能存在一个以上的空格,或者index和j并未相邻
|
|
|
|
|
#下边数字 = 0
|
|
|
|
|
#数值: 下边数字 = 当前数字, 当前数字 = 0
|
|
|
|
|
if board[index][col] == 0:
|
|
|
|
|
board[index][col] = board[row][col]
|
|
|
|
|
board[row][col] = 0
|
|
|
|
|
isAdd = 1
|
|
|
|
|
game_step += isAdd
|
|
|
|
|
return isAdd
|
|
|
|
|
|
|
|
|
|
#向左移动
|
|
|
|
|
def move_left():
|
|
|
|
|
global game_point
|
|
|
|
|
global game_step
|
|
|
|
|
global isAdd
|
|
|
|
|
isAdd = 0
|
|
|
|
|
for row in range(0,4):
|
|
|
|
|
index = 0
|
|
|
|
|
for col in range(1,4):
|
|
|
|
|
if board[row][col] > 0:
|
|
|
|
|
#当前数字等于下边数字
|
|
|
|
|
#下边数字 = 下边数字 + 当前数字, 当前数字 = 0
|
|
|
|
|
if board[row][col] == board[row][index]:
|
|
|
|
|
board[row][index] = board[row][col] + board[row][index]
|
|
|
|
|
game_point += board[row][col]
|
|
|
|
|
board[row][col] = 0
|
|
|
|
|
index += 1
|
|
|
|
|
isAdd = 1
|
|
|
|
|
#当前数字不等于下边数字
|
|
|
|
|
elif board[row][index] == 0:#当前数字上边有0
|
|
|
|
|
#下边数字 = 当前数字, 当前数字 = 0
|
|
|
|
|
board[row][index] = board[row][col]
|
|
|
|
|
board[row][col] = 0
|
|
|
|
|
isAdd = 1
|
|
|
|
|
else:
|
|
|
|
|
index += 1
|
|
|
|
|
#去除漏网之鱼
|
|
|
|
|
#index相当于慢指针,j相当于快指针
|
|
|
|
|
#也就是说快指针和慢指针中间可能存在一个以上的空格,或者index和j并未相邻
|
|
|
|
|
#下边数字 = 0
|
|
|
|
|
#数值: 下边数字 = 当前数字, 当前数字 = 0
|
|
|
|
|
if board[row][index] == 0:
|
|
|
|
|
board[row][index] = board[row][col]
|
|
|
|
|
board[row][col] = 0
|
|
|
|
|
isAdd = 1
|
|
|
|
|
game_step += isAdd
|
|
|
|
|
return isAdd
|
|
|
|
|
|
|
|
|
|
#向右移动
|
|
|
|
|
def move_right():
|
|
|
|
|
global game_point
|
|
|
|
|
global game_step
|
|
|
|
|
global isAdd
|
|
|
|
|
isAdd = 0
|
|
|
|
|
for row in range(0,4):
|
|
|
|
|
index = 3
|
|
|
|
|
for col in range(2,-1,-1):
|
|
|
|
|
if board[row][col] > 0:
|
|
|
|
|
#当前数字等于下边数字
|
|
|
|
|
#下边数字 = 下边数字 + 当前数字, 当前数字 = 0
|
|
|
|
|
if board[row][col] == board[row][index]:
|
|
|
|
|
board[row][index] = board[row][col] + board[row][index]
|
|
|
|
|
game_point += board[row][col]
|
|
|
|
|
board[row][col] = 0
|
|
|
|
|
index -= 1
|
|
|
|
|
isAdd = 1
|
|
|
|
|
#当前数字不等于下边数字
|
|
|
|
|
elif board[row][index] == 0:#当前数字上边有0
|
|
|
|
|
#下边数字 = 当前数字, 当前数字 = 0
|
|
|
|
|
board[row][index] = board[row][col]
|
|
|
|
|
board[row][col] = 0
|
|
|
|
|
isAdd = 1
|
|
|
|
|
else:
|
|
|
|
|
index -= 1
|
|
|
|
|
#去除漏网之鱼
|
|
|
|
|
#index相当于慢指针,j相当于快指针
|
|
|
|
|
#也就是说快指针和慢指针中间可能存在一个以上的空格,或者index和j并未相邻
|
|
|
|
|
#下边数字 = 0
|
|
|
|
|
#数值: 下边数字 = 当前数字, 当前数字 = 0
|
|
|
|
|
if board[row][index] == 0:
|
|
|
|
|
board[row][index] = board[row][col]
|
|
|
|
|
board[row][col] = 0
|
|
|
|
|
isAdd = 1
|
|
|
|
|
game_step += isAdd
|
|
|
|
|
return isAdd
|
|
|
|
|
|
|
|
|
|
#向上移动完全操作
|
|
|
|
|
def go_move_up():
|
|
|
|
|
global board_stack
|
|
|
|
|
global point_stack
|
|
|
|
|
global step_stack
|
|
|
|
|
|
|
|
|
|
global board
|
|
|
|
|
|
|
|
|
|
board2 = copy.deepcopy(board)
|
|
|
|
|
|
|
|
|
|
if move_up() == 1: #往上移 只有当移动成功了 才创建新的方块 并且把当前的情况入栈
|
|
|
|
|
create_new_block()
|
|
|
|
|
#入栈
|
|
|
|
|
board_stack.append(board2)
|
|
|
|
|
point_stack.append(get_point())
|
|
|
|
|
step_stack.append(1)
|
|
|
|
|
#产生声音
|
|
|
|
|
sound.slide_sound()
|
|
|
|
|
|
|
|
|
|
#向下移动完全操作
|
|
|
|
|
def go_move_down():
|
|
|
|
|
global board_stack
|
|
|
|
|
global point_stack
|
|
|
|
|
global step_stack
|
|
|
|
|
|
|
|
|
|
global board
|
|
|
|
|
|
|
|
|
|
board2 = copy.deepcopy(board)
|
|
|
|
|
|
|
|
|
|
if move_down() == 1: #往下移 只有当移动成功了 才创建新的方块 并且把当前的情况入栈
|
|
|
|
|
create_new_block()
|
|
|
|
|
#入栈
|
|
|
|
|
board_stack.append(board2)
|
|
|
|
|
point_stack.append(get_point())
|
|
|
|
|
step_stack.append(2)
|
|
|
|
|
#产生声音
|
|
|
|
|
sound.slide_sound()
|
|
|
|
|
|
|
|
|
|
#向左移动完全操作
|
|
|
|
|
def go_move_left():
|
|
|
|
|
global board_stack
|
|
|
|
|
global point_stack
|
|
|
|
|
global step_stack
|
|
|
|
|
|
|
|
|
|
global board
|
|
|
|
|
|
|
|
|
|
board2 = copy.deepcopy(board)
|
|
|
|
|
|
|
|
|
|
if move_left() == 1: #往左移 只有当移动成功了 才创建新的方块 并且把当前的情况入栈
|
|
|
|
|
create_new_block()
|
|
|
|
|
#入栈
|
|
|
|
|
board_stack.append(board2)
|
|
|
|
|
point_stack.append(get_point())
|
|
|
|
|
step_stack.append(3)
|
|
|
|
|
#产生声音
|
|
|
|
|
sound.slide_sound()
|
|
|
|
|
|
|
|
|
|
#向右移动完全操作
|
|
|
|
|
def go_move_right():
|
|
|
|
|
global board_stack
|
|
|
|
|
global point_stack
|
|
|
|
|
global step_stack
|
|
|
|
|
|
|
|
|
|
global board
|
|
|
|
|
|
|
|
|
|
board2 = copy.deepcopy(board)
|
|
|
|
|
|
|
|
|
|
if move_right() == 1: #往右移 只有当移动成功了 才创建新的方块 并且把当前的情况入栈
|
|
|
|
|
create_new_block()
|
|
|
|
|
#入栈
|
|
|
|
|
board_stack.append(board2)
|
|
|
|
|
point_stack.append(get_point())
|
|
|
|
|
step_stack.append(4)
|
|
|
|
|
#产生声音
|
|
|
|
|
sound.slide_sound()
|
|
|
|
|
|
|
|
|
|
#开始基础游戏(后面可能还会添加别的功能)
|
|
|
|
|
def start_base_game():
|
|
|
|
|
#定义全局变量
|
|
|
|
|
global game_step
|
|
|
|
|
global game_point
|
|
|
|
|
global isAdd
|
|
|
|
|
|
|
|
|
|
global board_stack
|
|
|
|
|
global point_stack
|
|
|
|
|
global step_stack
|
|
|
|
|
|
|
|
|
|
global board
|
|
|
|
|
|
|
|
|
|
board2 = copy.deepcopy(board)
|
|
|
|
|
#清空地图 + 创建两个格子
|
|
|
|
|
clear_map()
|
|
|
|
|
create_new_block()
|
|
|
|
|
create_new_block()
|
|
|
|
|
#初始化变量
|
|
|
|
|
game_step = 0
|
|
|
|
|
game_point = 0
|
|
|
|
|
isAdd = 0
|
|
|
|
|
|
|
|
|
|
##清空所有的栈
|
|
|
|
|
board_stack = []
|
|
|
|
|
point_stack = []
|
|
|
|
|
step_stack = []
|
|
|
|
|
##往栈中添加当前的元素
|
|
|
|
|
board_stack.append(board2)
|
|
|
|
|
point_stack.append(get_point())
|
|
|
|
|
step_stack.append(0)
|
|
|
|
|
#声音的处理
|
|
|
|
|
sound.stop_sound()
|
|
|
|
|
sound.background_sound()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ball_x = 0
|
|
|
|
|
ball_y = 0
|
|
|
|
|
move_enable = 0
|
|
|
|
|
#基础游戏进行时
|
|
|
|
|
def base_game_going():
|
|
|
|
|
global ball_x,ball_y,move_enable
|
|
|
|
|
global ai_delay_time
|
|
|
|
|
global sound_flag
|
|
|
|
|
#游戏正常
|
|
|
|
|
if judge_gameover() == False and judge_gamewin() == False:
|
|
|
|
|
sound_flag = 0
|
|
|
|
|
for event in pygame.event.get():
|
|
|
|
|
#按钮事件检测
|
|
|
|
|
buttonBase.check_event(event)
|
|
|
|
|
buttonReturn.check_event(event)
|
|
|
|
|
buttonAI.check_event(event)
|
|
|
|
|
buttonTips.check_event(event)
|
|
|
|
|
buttonReshow.check_event(event)
|
|
|
|
|
|
|
|
|
|
#键盘事件 基本键盘操作
|
|
|
|
|
if event.type == pygame.KEYDOWN:
|
|
|
|
|
if(event.key == pygame.K_LEFT or event.key == pygame.K_a):
|
|
|
|
|
go_move_left()
|
|
|
|
|
|
|
|
|
|
if(event.key == pygame.K_RIGHT or event.key == pygame.K_d):
|
|
|
|
|
go_move_right()
|
|
|
|
|
|
|
|
|
|
if(event.key == pygame.K_UP or event.key == pygame.K_w):
|
|
|
|
|
go_move_up()
|
|
|
|
|
|
|
|
|
|
if(event.key == pygame.K_DOWN or event.key == pygame.K_s):
|
|
|
|
|
go_move_down()
|
|
|
|
|
|
|
|
|
|
if(event.key == pygame.K_z):#Z键加快速度
|
|
|
|
|
if ai_delay_time > 50:
|
|
|
|
|
ai_delay_time = ai_delay_time - 50
|
|
|
|
|
if(event.key == pygame.K_x):#X键减慢速度
|
|
|
|
|
if ai_delay_time < 1000:
|
|
|
|
|
ai_delay_time = ai_delay_time + 50
|
|
|
|
|
#鼠标事件 滑动鼠标手势
|
|
|
|
|
elif event.type == pygame.MOUSEBUTTONDOWN:
|
|
|
|
|
ball_x,ball_y = pygame.mouse.get_pos()
|
|
|
|
|
if ball_x > start_x and ball_x < start_x + box_size*4: #限制鼠标手势范围
|
|
|
|
|
if ball_y > top_of_screen and ball_y < top_of_screen + box_size*4:
|
|
|
|
|
move_enable = 1
|
|
|
|
|
#print(ball_x,ball_y)
|
|
|
|
|
|
|
|
|
|
elif event.type == pygame.MOUSEBUTTONUP:
|
|
|
|
|
if move_enable == 1:
|
|
|
|
|
move_enable = 0
|
|
|
|
|
ball_x_2, ball_y_2 =pygame.mouse.get_pos()
|
|
|
|
|
dX = ball_x_2 - ball_x
|
|
|
|
|
dY = ball_y_2 - ball_y
|
|
|
|
|
|
|
|
|
|
if abs(dX) > abs(dY):
|
|
|
|
|
if dX < 0:
|
|
|
|
|
go_move_left()
|
|
|
|
|
else:
|
|
|
|
|
go_move_right()
|
|
|
|
|
|
|
|
|
|
elif abs(dX) < abs(dY):
|
|
|
|
|
if dY < 0:
|
|
|
|
|
go_move_up()
|
|
|
|
|
elif dY > 0:
|
|
|
|
|
go_move_down()
|
|
|
|
|
|
|
|
|
|
#游戏失败
|
|
|
|
|
elif judge_gameover() == True:
|
|
|
|
|
if sound_flag == 0:
|
|
|
|
|
sound_flag = 1
|
|
|
|
|
sound.stop_sound()
|
|
|
|
|
sound.fail_sound()
|
|
|
|
|
for event in pygame.event.get():
|
|
|
|
|
#按钮事件检测
|
|
|
|
|
buttonBase.check_event(event)
|
|
|
|
|
buttonReturn.check_event(event)
|
|
|
|
|
buttonAI.check_event(event)
|
|
|
|
|
buttonTips.check_event(event)
|
|
|
|
|
buttonReshow.check_event(event)
|
|
|
|
|
|
|
|
|
|
#游戏成功
|
|
|
|
|
elif judge_gamewin() == True:
|
|
|
|
|
if sound_flag == 0:
|
|
|
|
|
sound_flag = 1
|
|
|
|
|
sound.stop_sound()
|
|
|
|
|
sound.win_sound()
|
|
|
|
|
for event in pygame.event.get():
|
|
|
|
|
#按钮事件检测
|
|
|
|
|
buttonBase.check_event(event)
|
|
|
|
|
buttonReturn.check_event(event)
|
|
|
|
|
buttonAI.check_event(event)
|
|
|
|
|
buttonTips.check_event(event)
|
|
|
|
|
buttonReshow.check_event(event)
|
|
|
|
|
|
|
|
|
|
#获取空白方格
|
|
|
|
|
def get_empty():
|
|
|
|
|
lst = []
|
|
|
|
|
for row in range(4):
|
|
|
|
|
for col in range(4):
|
|
|
|
|
if board[row][col] == 0:
|
|
|
|
|
lst.append([row, col])
|
|
|
|
|
return lst
|
|
|
|
|
|
|
|
|
|
#判断游戏是否结束
|
|
|
|
|
def judge_gameover():
|
|
|
|
|
#当空白空格不为空时,即游戏未结束
|
|
|
|
|
if isEmpty() == True:
|
|
|
|
|
return False
|
|
|
|
|
#当空白方格为空时,判断是否存在可合并的方格
|
|
|
|
|
for row in range(4):
|
|
|
|
|
for col in range(3):
|
|
|
|
|
if board[row][col] == board[row][col + 1]:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
for row in range(3):
|
|
|
|
|
for col in range(4):
|
|
|
|
|
if board[row][col] == board[row + 1][col]:
|
|
|
|
|
return False
|
|
|
|
|
#若不满足以上两种情况,则游戏结束
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
#判断游戏是否成功
|
|
|
|
|
def judge_gamewin():
|
|
|
|
|
#检查是否有2048
|
|
|
|
|
maxnum = 0
|
|
|
|
|
for row in range(0,4):
|
|
|
|
|
for col in range(0,4):
|
|
|
|
|
if(board[row][col] > maxnum):
|
|
|
|
|
maxnum = board[row][col]
|
|
|
|
|
|
|
|
|
|
if maxnum == 2048:
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
#撤回功能回调函数(用于给按键事件使用)
|
|
|
|
|
def return_callback():
|
|
|
|
|
#定义全局变量
|
|
|
|
|
global game_step
|
|
|
|
|
global game_point
|
|
|
|
|
global isAdd
|
|
|
|
|
|
|
|
|
|
global board_stack
|
|
|
|
|
global point_stack
|
|
|
|
|
global step_stack
|
|
|
|
|
|
|
|
|
|
global board
|
|
|
|
|
sound.stop_sound()
|
|
|
|
|
sound.background_sound()
|
|
|
|
|
if game_step != 0:
|
|
|
|
|
board = board_stack[-1]
|
|
|
|
|
del board_stack[-1]
|
|
|
|
|
|
|
|
|
|
game_point = point_stack[-1]
|
|
|
|
|
del point_stack[-1]
|
|
|
|
|
|
|
|
|
|
del step_stack[-1]
|
|
|
|
|
|
|
|
|
|
game_step -= 1
|
|
|
|
|
sound.return_sound()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|