首页 > 其他分享 >2023APA招新游戏代码讲解

2023APA招新游戏代码讲解

时间:2023-10-12 21:26:59浏览次数:32  
标签:招新 point self 2023APA range pygame 讲解 line def

0 概况

招新小游戏是使用C++与Python设计的几款小游戏,并使用Pyside2设计GUI界面。对于萌新们来说,短时间学会游戏开发并不是很实际,本文旨在让大家简单体会游戏的开发过程,如何设计框架、调用模块、编写游戏以及实现代码封装,并不要求大家看完就能完全理解并具备开发游戏的能力。我们还是要从程序设计基础开始,经过APA团队2-3个月的C语言零基础训练,掌握编写程序的能力,感受程序底层的逻辑思维。学成之后再回过头来,自然会发现这样的游戏设计原来是这么简单且有趣的过程呀。

tips:关于密钥生成的规律在章节4.2

1 2048

image-20231006212024453

该游戏是基于pygame库设计的一款简单小游戏。

关于pygame的介绍以及基本用法,详情可见:

游戏设计的主体模块包括:

  • 可视化界面和键盘交互
  • 随机生成新的数字方块
  • 数字方块的合并
  • 输赢判定
(1)可视化界面和键盘交互

​ 通过pygame提供的创建窗口的方法,我们可以很方便地创建出和用户交互的可视化窗口。

class Game2048:
    def __init__(self):
        # 初始化 Pygame 库
        pygame.init()  
        # 创建游戏窗口
        self.screen = pygame.display.set_mode((WIDTH, HEIGHT))  
        # 设置窗口标题
        pygame.display.set_caption("2048")  
        # 创建一个 4x4 的游戏棋盘
        self.board = [[0] * 4 for _ in range(4)]  

​ 定义run()方法,将用户按键盘操作或点击关闭窗口转化为pygame的事件对象,最后在主函数中进行调用。

class Game2048:
    def run(self):
        while True: 
            # 遍历当前的事件列表
            for event in pygame.event.get():  
                # 如果事件类型是关闭窗口
                if event.type == pygame.QUIT:  
                    # 退出 Pygame
                    pygame.quit()  
                    return 
                 # 如果事件类型是键盘按键按下,移动游戏棋盘
                elif event.type == pygame.KEYDOWN: 
                    if event.key == pygame.K_UP: 
                        self.move("up")  
                    elif event.key == pygame.K_DOWN: 
                        self.move("down") 
                    elif event.key == pygame.K_LEFT: 
                        self.move("left")  
                    elif event.key == pygame.K_RIGHT:  
                        self.move("right") 
(2)随机生成新的数字方块

​ 使用random模块在随机位置生成数字。

class Game2048:
    def add_random_number(self):
        # empty_cells是判定出来的空单元格位置
        if empty_cells:
            i, j = random.choice(empty_cells)  # 随机选择一个空单元格
            self.board[i][j] = random.choice([2, 4])  # 在选择的空单元格中随机放置一个数字(2或4)
(3)数字方块的合并
class Game2048:
    def merge(self, line):
         # 标记是否已经合并过
        merged = False 
         # 存储合并后的新行
        new_line = [] 
        for i in range(len(line)):
            # 如果当前位置不为0
            if line[i] != 0:  
                # 如果新行不为空且新行的最后一个元素未合并
                if len(new_line) > 0 and new_line[-1] == line[i] and not merged:
                    new_line[-1] *= 2  # 将新行的最后一个元素翻倍
                    merged = True  # 标记已经合并过
                else:
                    # 将当前元素添加到新行中
                    new_line.append(line[i])  
                    # 标记未合并过
                    merged = False  
        # 补充0,使新行与原行长度相同
        new_line += [0] * (len(line) - len(new_line)) 
        # 返回合并后的新行
        return new_line  
(4)输赢判定

​ 如果有方块达到了2048或者所有方块布满了游戏界面而无法再移动了,游戏结束。

​ flag标记在__init__()初始化方法中中被定义,在check_game_over()通过类属性返回给run()函数,实现游戏结束。

class Game2048:
    def check_game_over(self):
        for i in range(4):
            for j in range(4):
                # 某个格子数字达到了胜利数字WIN_NUMBER
                if self.board[i][j] == WIN_NUMBER:
                    self.game_over = True
                    Game2048.flag = True
                    return
                
		# 判定格子是否满且可以移动
        for i in range(4):
            for j in range(4):
                if self.board[i][j] == 0:
                    Game2048.flag = False
                    return

        for i in range(3):
            for j in range(3):
                if self.board[i][j] == self.board[i + 1][j] or self.board[i][j] == self.board[i][j + 1]:
                    Game2048.flag = False
                    return

        for i in range(3):
            if self.board[i][3] == self.board[i + 1][3]:
                Game2048.flag = False
                return

        for j in range(3):
            if self.board[3][j] == self.board[3][j + 1]:
                Game2048.flag = False
                return
            
        # 格子满且无法移动
        self.game_over = True

2 加入APA(逃离CPPU)

image-20231006212024453

第二个游戏用的是C++,并调用了图形引擎EGE(Easy Graphics Engine)。EGE是 windows 下的简易绘图库,是一个面向 C/C++ 语言新手的图形库。EGE的安装与使用可以参考

从官网上下下来之后简单改一下配置就可以用了。

EGE非常简单友好,容易上手,而且接口意义直观,程序主要用了EGE的几个函数:

//一、界面操作
initgraph(800,600);//初始化一个800*600的界面
closegraph();//关闭图形界面
//二、图形操作
setfillcolor(BLACK);//设置颜色
bar(1,1,800,600);//在指定位置画矩阵(参数为左上、右下坐标)
circle(1,1,100);//在指定位置画圆(参数为圆形坐标、半径)
//三、文字操作
setfont(40,0,"幼圆");//设置字体大小40,幼圆格式
outtextxy(200, 350,"XXX");//在指定位置输出文本

完整代码放在文末链接里,这边用伪代码简单展示一下程序整体思路

void doing(){
	while (1){
	    //每0.1秒为一帧
	    Sleep(100);
	    
		//移动小红,并重新打印小红的新位置 
    	for (i=1;i<=30;i++)
    	    删除原位置小红;
    		move(red[i]);
			打印新位置小红;
			
    	// 当识别到WASD被按下时,改变小蓝方向 
		if (kbhit()) {
			n=getch();
			if (n=='d') blue.w=0;
			if (n=='a') blue.w=1;
			if (n=='s') blue.w=2;
			if (n=='w') blue.w=3;
		}
		
		// 移动小蓝,重新打印 
		删除原位置小蓝;
		move(blue);
		打印新位置小蓝
		
        //逐一检测移动后小蓝有没有撞到小红 
        for (i=1;i<=30;i++)	{
        	if 如果小蓝撞到了小红 { 
		    	hp--; //扣血 
		    	if (hp==0)
		    	    gameover;
		    	    cout<<0;
		    	    return;
				else
				    显示扣血提示;
				    getch();
		    		//撞到的小红要移到其他地方,不然复活后会秒寄 
            		rebuild(red[i])
            		break;
	       	}
		}
		
		if (小蓝达到终点) 
		    congratulations;
		    cout<<1;
            return;
		
		//打印地图其他信息
		打印终点;
    	在左上角打印HP值; 
	}
}

int main(){
    //标记是否通关
	freopen("flag.txt","w",stdout);
	//初始化地图;
	initgraph(800,600);
	生成起点终点的位置;
    生成小蓝坐标与方向速度;
    for (i=1;i<=30;i++) {
        生成小红坐标与方向速度;
        if (有重合) 重新生成;
    }
    //游戏运行函数 
	doing();	
	//关闭图形界面
    closegraph(); 
}

3 五子棋

image-20231006212024453

游戏同样使用的pygame库,主体模块包括:

  • 可视化界面和键盘交互
  • 人机交互
  • 生成AI落子点
  • 输赢判定
(1)可视化界面

定义Checkerboard棋盘类进行棋盘控制与人际交互

class Checkerboard:
    # 初始化棋盘界面
    def __init__(self, line_points):
        self._line_points = line_points
        self._checkerboard = [[0] * line_points for _ in range(line_points)]
    def _get_checkerboard(self):
        return self._checkerboard
    checkerboard = property(_get_checkerboard)
    
    # 进行落子
    def drop(self, chessman, point):
        self._checkerboard[point.Y][point.X] = chessman.Value
        
# 棋盘绘制
def _draw_checkerboard(screen):
    screen.fill(Checkerboard_Color)
    # 画棋盘网格线外的边框
    pygame.draw.rect(screen, BLACK_COLOR, (Outer_Width, Outer_Width, Border_Length, Border_Length), Border_Width)
    # 画网格线
    for i in range(Line_Points):
        pygame.draw.line(screen, BLACK_COLOR,(Start_Y, Start_Y + SIZE * i), (Start_Y + SIZE * (Line_Points - 1), Start_Y + SIZE * i), 1)
    for j in range(Line_Points):
        pygame.draw.line(screen, BLACK_COLOR, (Start_X + SIZE * j, Start_X),  (Start_X + SIZE * j, Start_X + SIZE * (Line_Points - 1)), 1)
(2)人机交互
# 根据鼠标点击位置,返回游戏区坐标
def _get_clickpoint(click_pos):
    pos_x = click_pos[0] - Start_X
    pos_y = click_pos[1] - Start_Y
    if pos_x < -Inside_Width or pos_y < -Inside_Width:
        return None
    x = pos_x // SIZE
    y = pos_y // SIZE
    if pos_x % SIZE > Stone_Radius:
        x += 1
    if pos_y % SIZE > Stone_Radius:
        y += 1
    if x >= Line_Points or y >= Line_Points:
        return None

    return Point(x, y)
(3)生成AI落子点

AI整体是一套贪心的思路,对于每一个落子点,计算在此处落子的价值(将八个方向的价值累加),最终选择价值最高的落子点落子

class AI:
    # 初始化
    def __init__(self, line_points, chessman):
        self._line_points = line_points
        self._my = chessman
        self._opponent = BLACK_CHESSMAN if chessman == WHITE_CHESSMAN else WHITE_CHESSMAN
        self._checkerboard = [[0] * line_points for _ in range(line_points)]
	
    # 判断落子价值
    def AI_drop(self):
        point = None
        score = 0
        for i in range(self._line_points):
            for j in range(self._line_points):
                if self._checkerboard[j][i] == 0:
                    _score = self._get_point_score(Point(i, j))
                    if _score > score:
                        score = _score
                        point = Point(i, j)
                    elif _score == score and _score > 0:
                        r = random.randint(0, 100)
                        if r % 2 == 0:
                            point = Point(i, j)
        self._checkerboard[point.Y][point.X] = self._my.Value
        return point
    
	# 累加八个方向的价值分
    def _get_point_score(self, point):
        score = 0
        for os in offset:
            score += self._get_direction_score(point, os[0], os[1])
        return score

    def _get_direction_score(self, point, x_offset, y_offset):
        # 根据该方向下双方连续落子数、有无空格以及两端有无阻挡,判定落子与该点的方向价值分数
(4)输赢判断

在棋盘类型中添加输赢判断函数

def _win(self, point):
    cur_value = self._checkerboard[point.Y][point.X]
    for os in offset:
        if self._get_count_on_direction(point, cur_value, os[0], os[1]):
            return True

def _get_count_on_direction(self, point, value, x_offset, y_offset):
    count = 1
    for step in range(1, 5):
        x = point.X + step * x_offset
        y = point.Y + step * y_offset
        if 0 <= x < self._line_points and 0 <= y < self._line_points and self._checkerboard[y][x] == value:
            count += 1
        else:
            break
    for step in range(1, 5):
        x = point.X - step * x_offset
        y = point.Y - step * y_offset
        if 0 <= x < self._line_points and 0 <= y < self._line_points and self._checkerboard[y][x] == value:
            count += 1
        else:
            break
    return count >= 5

4 程序封装

4.1 可视化界面

使用Pyside2进行封装,安装可参考

安装后使用插件Qt Designer进行可视化编辑界面,生成ui文件(背景图好像只支持png格式)
image-20231006212024453

将ui文件导入python中:

app = QApplication([])
ui = QUiLoader().load('./b.ui')
ui.show()
app.exec_()

将五个按钮与python函数一一连接:

ui.pushButton.clicked.connect(game1)
ui.pushButton_2.clicked.connect(game2)
ui.pushButton_3.clicked.connect(game3)
ui.pushButton_4.clicked.connect(loadin)
ui.pushButton_5.clicked.connect(get_key)
4.2 秘钥生成

秘钥其实只用了一个最简单的凯撒加密,即将明文中的所有字母按字母表上向后进行偏移K位后,被替换成密文。例如,当K=3时,A将被替换成D,B变成E,Z则变成C。本次加密的K值选取了用户名的长度,确保每一个用户名对应的秘钥是唯一的。

将用户名凯撒加密后,与通关数(0关-A; 1关-B; 2关-C; 3关-D)和K值相拼接,即得到最终秘钥。

举例来说:假设用户“CPPUAPA”通过了2关。“CPPUAPA”的长度为7,则对“CPPUAPA”进行7位凯撒加密,得到“JWWBHWH”;通过2关,代码为“C”;K值为7,对应字母为“H"。最后的秘钥应为”JWWBHWHCG“(字母均默认转换为大写)

cin>>name;
for (int i=0;i<s.length();i++){
	if (name[i]>='a' && name[i]<='z'){
        name[i] = char(name[i]+32)// 小写转大写
    }
	key[i]=char((name-'A'+K)%26+'A')// 加K位
}
key = key + chr(grade+'A') + chr(K+'A')
cout<<key;
4.3 备份json文件

对所有玩家的游戏记录和秘钥进行备份,存储于game_record.json中

{"user": "Wielttgest", "game1": 1, "game2": 0, "key": "GSOVDDQOCDBK"}

load按钮用于生成新的json对象,放于文件中。

def loadin():
    # 读入文本框内的用户名
    user = ui.textEdit.toPlainText()
    # 同名检查
    with open('game_record.json', 'r') as f:
        data = f.readlines()
    for d in data:
        dict = json.loads(d)
        if (dict["user"]==user):
            print("已经有同名的人了,请在用户名后加字母后缀~")
            return
    # 同名检查通过,添加至json中
    dict = {"user": user}
    with open('game_record.json', 'a') as f:
        f.write('\n')
        json.dump(dict, f)
    print(user, "has load in")

之后每一次游戏结束后,将通关与否的标记自动添加至对应json对象中。如果玩家同一关卡已经有记录,说明已经是第二次玩了,则不做记录。

def add_value(user,name,value
    for i in range(len(data)):
        d = data[i]
        dict = json.loads(d)
        if (dict["user"]==user): # 找到该用户
            if name not in dict: # 判断是否玩过
                dict[name] = value # 添加
            elif (name != "key"):
                print("你已经玩过一次啦,成绩不算哦")

判断用户通关了几关,有跳关则不做记录。

def get_grade(user):
    grade = 0
    if ("game1" in data and now["game1"]):
        grade = 1
        if ("game2" in data and now["game2"]):
            grade = 2
            if ("game3" in data and now["game3"]):
                grade = 3
    return grade

Author:张霁阳 熊天诚

标签:招新,point,self,2023APA,range,pygame,讲解,line,def
From: https://www.cnblogs.com/rachel0701/p/17760585.html

相关文章

  • PHP 操作redis 详细讲解转的
    phpredis是redis的php的一个扩展,效率是相当高有链表排序功能,对创建内存级的模块业务关系很有用;以下是redis官方提供的命令使用技巧:https://www.clw9335.com/gl/719374.html下载地址如下:https://github.com/owlient/phpredis(支持redis2.0.4)Redis::__construct构造函数$redi......
  • YOLOv8+DeepSORT多目标车辆跟踪(车辆检测+跟踪+车辆计数)(内附免费资源+部署讲解)
    https://blog.csdn.net/Little_Carter/article/details/133610076目录一、前言二、开发环境(前提条件)三、环境搭建教程3.1、创建虚拟环境3.2、选择虚拟环境并安装所需要的包3.3、运行代码步骤3.3.1、克隆git储存库3.3.2、转到克隆库的文件夹下3.3.3、安装依赖项3.3.4......
  • 传奇服务端Mirserver功能讲解
    DBServer(数据库服务端)ConnectionFDB(人物数据库,数据库格式为传奇自定义格式)Log(角色选择服务端日志)AddrTable.txt(IP地址配置)!IdList.txt(缴费账号列表,!Setup.exe中ServiceMode=TRUE时起作用)!ServerInfo.txt(IP地址配置)DBServer.exe(数据库主程序)DBSrc.ini(数据库主程序配......
  • 复习课15 C语言作业讲解
    一.选择题1.以下哪一项不属于C语言内置的数据类型()A.intB.shortC.structStrD.float答案:C解析:C语言中内置的数据类型有:intshortfloatdoublelongchar等,并不包含C选项中的structStr,故选C2.局部变量的作用域是()A.main()函数内部B.整个程序C.main()函数之前D.局部变量所在地局部......
  • SpringBoot集成WebSocket讲解
    目录1WebSocket1.1简介1.2WebSocket作用和调用1.2.1作用1.2.2js端调用1.3Javax1.3.1服务端1.3.1.1服务端接收1.3.1.2服务端集成1.3.1.3ping和pong消息1.3.2客户端1.3.2.1客户端接收1.3.2.2客户端发送1.4WebMVC1.4.1服务端1.1.4.1服务端接收1.1.4.2服务端集成1.1......
  • 斐波那列数列的讲解过程
    python案例解释的有点不好,多多包含deff1(n):ifn<=2:return1;else:returnf1(n-1)+f1(n-2)#print(f1(6))"""示例1解释一下他是如何等8的,递归不是直接返回值再去传递给自身函数,比如n=4的时候,那么f1(4-1)+f1(4-2)=f1(3)+f1(2)不是......
  • CountDownLatch、CyclicBarrier、Semaphore面试讲解
    @TOC<hrstyle="border:solid;width:100px;height:1px;"color=#000000size=1">这三个也是面试常问的,作为线程通信的方法1.CountDownLatch(CDL)主要是用于一个线程等待其他完成后才继续执行。主要方法:await()、countDown()CountDownLatchcdl=newCountDownLatch(2);//第一......
  • Serverless平台knative第六章配置最大并发数及更新操作讲解
    并发数配置apiVersion:serving.knative.dev/v1kind:Servicemetadata:name:hellospec:template:metadata:name:hello-world-002spec:containerConcurrency:10#单个pod允许的最大并发数,超过将扩容containers:#-image:gcr......
  • 微软写了份GPT-4V说明书:166页详细讲解,提示词demo示例全都有
    克雷西萧箫发自凹非寺量子位公众号QbitAI多模态王炸大模型GPT-4V,166页“说明书”重磅发布!而且还是微软团队出品。什么样的论文,能写出166页?不仅详细测评了GPT-4V在十大任务上的表现,从基础的图像识别、到复杂的逻辑推理都有展示;还传授了一整套......
  • lesson6课堂练习与讲解 (布局实战)
     packagecom.zym.lesson6;importjava.awt.*;importjava.awt.event.WindowAdapter;importjava.awt.event.WindowEvent;publicclassTestMixLayOut{publicstaticvoidmain(String[]args){Frameframe=newFrame("课堂练习,混合布局");......