纯Java实现坦克大战游戏,源码公开,面试前端开发自我介绍

66 阅读5分钟

算法

  1. 冒泡排序

  2. 选择排序

  3. 快速排序

  4. 二叉树查找: 最大值、最小值、固定值

  5. 二叉树遍历

  6. 二叉树的最大深度

  7. 给予链表中的任一节点,把它删除掉

  8. 链表倒叙

  9. 如何判断一个单链表有环

由于篇幅限制小编,pdf文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!

开源分享:docs.qq.com/doc/DSmRnRG… //取出炸弹 Bomb b=bombs.get(i); if(b.life>6){ g.drawImage(image1, b.x, b.y, 30, 30, this); }else if(b.life>3){ g.drawImage(image2, b.x, b.y, 30, 30, this); }else { g.drawImage(image3, b.x, b.y, 30, 30, this); } //让b的生命值减少 b.lifeDown(); //如果炸弹生命值为0,就把该炸弹从bombs向量中去掉 if(b.life==0){ bombs.remove(b); } }

	//画出敌人的坦克
	for(int i=0;i<ets.size();i++){
		EnemyTank et=ets.get(i);
		if(et.isLive){
			this.drawTank(et.getX(), et.getY() , g, 1, et.getDirect());
			//再画出敌人坦克的子弹
			for(int j=0;j<et.ss.size();j++){
				//取出子弹
				Shot enemyShot=et.ss.get(j);
				if(enemyShot.isLive){
					g.draw3DRect(enemyShot.x, enemyShot.y, 1, 1, false);
				}else{
					//如果敌人的坦克死亡了就从向量Vector中去掉
					et.ss.remove(enemyShot);
				}
			}
		}
	}
}

//判断我的子弹是否击中敌人的坦克
public void hitEnemyTank(){
	//遍历Vector集合类
	for(int i=0;i<hero.ss.size();i++){
		//先取子弹
		Shot myShot=hero.ss.get(i);
		//判断子弹是否有效
		if(myShot.isLive){
			//取出每一个敌人坦克与子弹进行判断
			for(int j=0;j<ets.size();j++){
				//取出坦克
				EnemyTank et=ets.get(j);
				//判断敌人坦克是否还活着
				if(et.isLive){
					if(this.hitTank(myShot, et)){
						apw=new AePlayWave("e:\\tankgame\\tank_explosion.wav");
						apw.start();
						//调用reduceEnNum()减少敌人坦克统计数
						Recorder.reduceEnNum();
						//调用addEnNumRec()增加消灭敌人坦克统计数
						Recorder.addEnNumRec();
					}
				}
			}
		}
	}
}

//判断敌人的子弹是否击中我的坦克
public void hitMe(){
	//取出每一个敌人的坦克
	for(int i=0;i<this.ets.size();i++){
		//取出敌人的坦克
		EnemyTank et=ets.get(i);
		//取出每一颗敌人的子弹
		for(int j=0;j<et.ss.size();j++){
			//取出子弹
			Shot enemyShot=et.ss.get(j);
			
			if(hero.isLive){
				if(this.hitTank(enemyShot, hero)){
					
				}
			}
		}
	}
}

//写一个函数专门判断子弹是否击中敌人坦克
public boolean hitTank(Shot s,Tank et){
	boolean b=false;
	//判断该敌人坦克的方向
	switch(et.direct){
	case 0://敌人坦克向上或向下
	case 1:
		if(s.x>et.x&&s.x<et.x+20&&s.y>et.y&&s.y<et.y+29){
			//击中方向为上或下的敌人坦克
			//子弹死亡
			s.isLive=false;
			//敌人坦克死亡
			et.isLive=false;
			b=true;
			//创建一颗炸弹
			Bomb bomb=new Bomb(et.x, et.y);
			//放入Vector向量中管理
			bombs.add(bomb);
		}
		break;
	case 2://敌人坦克向左或向右
	case 3:
		if(s.x>et.x&&s.x<et.x+29&&s.y>et.y&&s.y<et.y+20){
			//击中方向为左或右的敌人坦克
			//子弹死亡
			s.isLive=false;
			//敌人坦克死亡
			et.isLive=false;
			b=true;
			//创建一颗炸弹
			Bomb bomb=new Bomb(et.x, et.y);
			//放入Vector向量中管理
			bombs.add(bomb);
		}
		break;
	}
	return b;
}

//画出坦克的函数
public void drawTank(int x,int y,Graphics g,int type,int direct){
	//判断是什么类型的坦克
	switch(type){
	case 0:
		g.setColor(Color.cyan);//我的坦克颜色
		break;
	case 1:
		g.setColor(Color.yellow);//敌人坦克颜色
		break;
	case 2:
		g.setColor(Color.red);
		break;
	}
	
	//判断坦克的方向
	switch(direct){
	//向上走的坦克
	case 0:
		//画出我的坦克(到时再封装成一个函数)
		//1、画出左边的矩形
		g.fill3DRect(x, y, 5, 30, false);
		//2、画出右边的矩形
		g.fill3DRect(x+15, y, 5, 30, false);
		//3、画出中间矩形
		g.fill3DRect(x+5, y+5, 10, 20, false);
		//4、画出中间圆形
		g.fillOval(x+5, y+10, 10, 10);
		//5、画出线(炮筒)
		g.drawLine(x+10, y+15, x+10, y);
		break;
	//向下走的坦克
	case 1:
		g.fill3DRect(x, y, 5, 30, false);
		g.fill3DRect(x+15, y, 5, 30, false);
		g.fill3DRect(x+5, y+5, 10, 20, false);
		g.fillOval(x+5, y+10, 10, 10);
		g.drawLine(x+10, y+15, x+10, y+29);
		break;
	//向左走的坦克
	case 2:
		g.fill3DRect(x, y, 30, 5, false);
		g.fill3DRect(x, y+15, 30, 5, false);
		g.fill3DRect(x+5, y+5, 20, 10, false);
		g.fillOval(x+10, y+5, 10, 10);
		g.drawLine(x+15, y+10, x, y+10);
		break;
	//向右走的坦克
	case 3:
		g.fill3DRect(x, y, 30, 5, false);
		g.fill3DRect(x, y+15, 30, 5, false);
		g.fill3DRect(x+5, y+5, 20, 10, false);
		g.fillOval(x+10, y+5, 10, 10);
		g.drawLine(x+15, y+10, x+29, y+10);
		break;
	}
}

public void keyPressed(KeyEvent e) {//按下键事件a向左s向下d向右w向上
	if(e.getKeyCode()==KeyEvent.VK_W||e.getKeyCode()==KeyEvent.VK_UP){
		//向上
		this.hero.setDirect(0);
		this.hero.moveUp();
	}else if(e.getKeyCode()==KeyEvent.VK_S||e.getKeyCode()==KeyEvent.VK_DOWN){
		//设置我的坦克的方向,向下
		this.hero.setDirect(1);
		this.hero.moveDown();
	}else if(e.getKeyCode()==KeyEvent.VK_A||e.getKeyCode()==KeyEvent.VK_LEFT){
		//向左
		this.hero.setDirect(2);
		this.hero.moveLeft();
	}else if(e.getKeyCode()==KeyEvent.VK_D||e.getKeyCode()==KeyEvent.VK_RIGHT){
		//向右
		this.hero.setDirect(3);
		this.hero.moveRight();
	}
	
	//判断玩家是否按下空格键,不可接上面else if。不然不能同时按方向键和空格(space)键
	if(e.getKeyCode()==KeyEvent.VK_SPACE){
		apw=new AePlayWave("e:\\tankgame\\tank_Shelling_sound.wav");
		apw.start();
		//控制子弹连发
		if(this.hero.ss.size()<=4){
			//按下空格后开火
			this.hero.shotEnemy();
		}
	}
	
	//调用repaint()函数,来重绘界面
	this.repaint();
}

public void keyReleased(KeyEvent e) {//弹起键事件
	
}

public void keyTyped(KeyEvent e) {//按键输出值
	
}

//重写run函数
public void run() {
	while(true){
		try {
			Thread.sleep(100);//休息100毫秒后重绘MyPanel面板
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		this.hitEnemyTank();
		//判断敌人的子弹是否击中我的坦克
		this.hitMe();

		//重绘MyPanel面板
		this.repaint();
	}
}

}


### 二、成员类



package com.haiding.tank_7;

import java.util.Vector; import java.io.; import javax.sound.sampled.;

//播放声音的类 class AePlayWave extends Thread { private String filename; public AePlayWave(String wavfile) { filename = wavfile; }

public void run() {
	File soundFile = new File(filename);
	AudioInputStream audioInputStream = null;
	try {
		audioInputStream = AudioSystem.getAudioInputStream(soundFile);
	} catch (Exception e1) {
		e1.printStackTrace();
		return;
	}

	AudioFormat format = audioInputStream.getFormat();
	SourceDataLine auline = null;
	DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);

	try {
		auline = (SourceDataLine) AudioSystem.getLine(info);
		auline.open(format);
	} catch (Exception e) {
		e.printStackTrace();
		return;
	}

	auline.start();
	int nBytesRead = 0;
	//这是缓冲
	byte[] abData = new byte[2048];

	try {
		while (nBytesRead != -1) {
			nBytesRead = audioInputStream.read(abData, 0, abData.length);
			if (nBytesRead >= 0)
				auline.write(abData, 0, nBytesRead);
		}
	} catch (IOException e) {
		e.printStackTrace();
		return;
	} finally {
		auline.drain();
		auline.close();
	}
}

}

//记录恢复点 class Node{ int x,y,direct; public Node(int x,int y,int direct){ this.x=x; this.y=y; this.direct=direct; } }

//记录类,同时也可以保存玩家的设置 class Recorder{ //记录每关有多少敌人 private static int enNum=20; //设置我有多少可用的人 private static int myLife=3; //记录总共消灭了多少敌人的坦克 private static int allEnNum=0; //从文件中恢复记录点 private static Vector nodes=new Vector();

private static FileWriter fw=null;
private static BufferedWriter bw=null;
private static FileReader fr=null;
private static BufferedReader br=null;
private static Vector<EnemyTank> ets=new Vector<EnemyTank>();

//完成读取任务点任务
public static Vector<Node> getNodesAndEnNums(){
	try {
		fr=new FileReader("e:\\tankgame\\tanksave.txt");
		br=new BufferedReader(fr);
		String n="";
		//先读一行
		n=br.readLine();
		allEnNum=Integer.parseInt(n);		
		while((n=br.readLine())!=null){
			String []Recovery=n.split(" ");//split方法可以按一行字符中有多少个空间来返回数组
			Node node=new Node(Integer.parseInt(Recovery[0]),Integer.parseInt(Recovery[1]),Integer.parseInt(Recovery[2]));
			nodes.add(node);
		}
	} catch (Exception e) {
		e.printStackTrace();
	}finally{
		try {
			br.close();
			fr.close();
		} catch (Exception e2) {
			e2.printStackTrace();
		}
	}
	return nodes;
}

//保存击毁敌人的数量和敌人坦克坐标、方向
public static void keepRecAndEnemyTank(){
	try {
		//创建
		fw=new FileWriter("e:\\tankgame\\tanksave.txt");
		bw=new BufferedWriter(fw);
		bw.write(allEnNum+"\r\n");
		
		//保存当前还活着的敌人坦克坐标、方向
		for(int i=0;i<ets.size();i++){
			//取出第一个坦克
			EnemyTank et=ets.get(i);
			if(et.isLive){
				//活的保存
				String recode=et.x+" "+et.y+" "+et.direct;//得到坐标x,y和方向direct
				//写入到文件
				bw.write(recode+"\r\n");
			}
		}
	} catch (Exception e) {
		e.printStackTrace();
	}finally{
		//关闭流,先开后关,后开先关
		try {
			bw.close();
			fw.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

//从文件中读取记录
public static void getRecoring(){
	try {
		fr=new FileReader("e:\\tankgame\\tanksave.txt");
		br=new BufferedReader(fr);
		String n=br.readLine();
		allEnNum=Integer.parseInt(n);
	} catch (Exception e) {
		e.printStackTrace();
	}finally{
		try {
			br.close();
			fr.close();
		} catch (Exception e2) {
			e2.printStackTrace();
		}
	}
}
//把玩家击毁敌人坦克数量保存到文件中
public static void keepRecording(){
	try {
		//创建
		fw=new FileWriter("e:\\tankgame\\tanksave.txt");
		bw=new BufferedWriter(fw);
		bw.write(allEnNum+"\r\n");
	} catch (Exception e) {
		e.printStackTrace();
	}finally{
		//关闭流,先开后关,后开先关
		try {
			bw.close();
			fw.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}


public static int getAllEnNum() {
	return allEnNum;
}
public static void setAllEnNum(int allEnNum) {
	Recorder.allEnNum = allEnNum;
}
public static int getEnNum() {
	return enNum;
}
public static void setEnNum(int enNum) {
	Recorder.enNum = enNum;
}
public static int getMyLife() {
	return myLife;
}
public static void setMyLife(int myLife) {
	Recorder.myLife = myLife;
}

public static Vector<EnemyTank> getEts() {
	return ets;
}

public static void setEts(Vector<EnemyTank> ets) {
	Recorder.ets = ets;
}

//敌人坦克死亡就减少坦克数
public static void reduceEnNum(){
	enNum--;
}
//当消灭敌人的时候
public static void addEnNumRec(){
	allEnNum++;
}

}

//炸弹类 class Bomb{ //定义炸弹的坐标 int x,y; int life=9;//炸弹的生命 boolean isLive=true; public Bomb(int x,int y){ this.x=x; this.y=y; } //减少炸弹生命值 public void lifeDown(){ if(life>0){ life--; }else{ this.isLive=false; } } }

//子弹类 class Shot implements Runnable{ int x,y,direct,speed=3;//初始子弹x,y坐标,direct子弹方向,speed子弹速度

//子弹是否还活着
boolean isLive=true;//默认为活着

public Shot(int x,int y,int direct){
	this.x=x;
	this.y=y;
	this.direct=direct;
}

public void run(){
	while(true){
		try {
			Thread.sleep(50);//让子弹休息50毫秒,防止快速消费内存
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		switch(direct){
			case 0://向上
				y-=speed;
				break;
			case 1://向下
				y+=speed;
				break;
			case 2://向 左
				x-=speed;
				break;
			case 3://向右
				x+=speed;
				break;
		}
		//子弹何时死亡
		//判断该子弹是否碰到MyPanel面板的边缘
		if(x<0||x>400||y<0||y>300){
			this.isLive=false;
			break;
		}
	}
}

}

//定义坦克类 class Tank{ //表示坦克的X横坐标Y纵坐标 int x=0,y=0; //direct坦克方向,0向上、1向下、2向左、3向右 int direct=0; //坦克的速度 int speed=1; //坦克颜色 int color;

boolean isLive=true;

public Tank(int x,int y){
	this.x=x;
	this.y=y;
}
public int getX() {
	return x;
}
public void setX(int x) {
	this.x = x;
}
public int getY() {
	return y;
}
public void setY(int y) {
	this.y = y;
}
public int getDirect() {
	return direct;
}
public void setDirect(int direct) {
	this.direct = direct;
}
public int getSpeed() {
	return speed;
}
public void setSpeed(int speed) {
	this.speed = speed;
}
public int getColor() {
	return color;
}
public void setColor(int color) {
	this.color = color;
}

}

//敌人坦克,把敌人坦克做成线程类 class EnemyTank extends Tank implements Runnable{ int move=30;//敌人坦克移动步长(区域) int times=0;

//定义一个向量,可以访问到MyPanel上所有敌人坦克
Vector<EnemyTank> ets=new Vector<EnemyTank>();
//定义一个向量,可以存放敌人的子弹
Vector<Shot> ss=new Vector<Shot>();
//敌人添加子弹,应在创建坦克和敌人的坦克子弹死亡后
public EnemyTank(int x,int y){
	super(x,y);
}
//得到MyPanel的敌人坦克向量
public void setEts(Vector<EnemyTank> tank){
	this.ets=tank;
}

//判断敌人坦克是否重叠
public boolean isTouchOtherEnemy(){
	boolean b=false;

	switch(this.direct){
	case 0://敌人当前坦克向上
		//取出所有敌人坦克
		for(int i=0;i<ets.size();i++){
			//取出第一个坦克
			EnemyTank et=ets.get(i);
			//如果不是当前的坦克
			if(et!=this){
				//如果敌人同伴的坦克向上或向下
				if(et.direct==0||et.direct==1){
					//判断敌人当前坦克左轮与同伴坦克(向上或向下)的位置比较
					if(this.x>=et.x&&this.x<=et.x+20&&this.y>=et.y&&this.y<=et.y+30){
						return true;
					}
					if(this.x+20>=et.x&&this.x+20<=et.x+20&&this.y>=et.y&&this.y<=et.y+30){
						return true;
					}
				}
				//如果敌人同伴的坦克向左或向右
				if(et.direct==2||et.direct==3){
					//判断敌人当前坦克右轮与同伴坦克(向左或向右)的位置比较
					if(this.x>=et.x&&this.x<=et.x+30&&this.y>=et.y&&this.y<=et.y+20){
						return true;
					}
					if(this.x+20>=et.x&&this.x+20<=et.x+30&&this.y>=et.y&&this.y<=et.y+20){
						return true;
					}
				}
			}
		}
		break;
	case 1://敌人当前坦克向下
		for(int i=0;i<ets.size();i++){
			EnemyTank et=ets.get(i);
			if(et!=this){
				if(et.direct==0||et.direct==1){
					if(this.x>=et.x&&this.x<=et.x+20&&this.y+30>=et.y&&this.y+30<=et.y+30){
						return true;
					}
					if(this.x+20>=et.x&&this.x+20<=et.x+20&&this.y+30>=et.y&&this.y+30<=et.y+30){
						return true;
					}
				}
				if(et.direct==2||et.direct==3){
					if(this.x>=et.x&&this.x<=et.x+30&&this.y+30>=et.y&&this.y+30<=et.y+20){
						return true;
					}
					if(this.x+20>=et.x&&this.x+20<=et.x+30&&this.y+30>=et.y&&this.y+30<=et.y+20){
						return true;
					}
				}
			}
		}
		break;
	case 2://敌人当前坦克向左
		for(int i=0;i<ets.size();i++){
			EnemyTank et=ets.get(i);
			if(et!=this){
				if(et.direct==0||et.direct==1){
					if(this.x>=et.x&&this.x<=et.x+20&&this.y>=et.y&&this.y<=et.y+30){
						return true;
					}
					if(this.x>=et.x&&this.x<=et.x+20&&this.y+20>=et.y&&this.y+20<=et.y+30){
						return true;
					}
				}
				if(et.direct==2||et.direct==3){
					if(this.x>=et.x&&this.x<=et.x+30&&this.y>=et.y&&this.y<=et.y+20){
						return true;
					}
					if(this.x>=et.x&&this.x<=et.x+30&&this.y+20>=et.y&&this.y+20<=et.y+20){
						return true;
					}
				}
			}
		}
		break;
	case 3://敌人当前坦克向右
		for(int i=0;i<ets.size();i++){
			EnemyTank et=ets.get(i);
			if(et!=this){
				if(et.direct==0||et.direct==1){
					if(this.x+30>=et.x&&this.x+30<=et.x+20&&this.y>=et.y&&this.y<=et.y+30){
						return true;
					}
					if(this.x+30>=et.x&&this.x+30<=et.x+20&&this.y+20>=et.y&&this.y+20<=et.y+30){
						return true;
					}
				}
				if(et.direct==2||et.direct==3){
					if(this.x+30>=et.x&&this.x+30<=et.x+30&&this.y>=et.y&&this.y<=et.y+20){
						return true;
					}
					if(this.x+30>=et.x&&this.x+30<=et.x+30&&this.y+20>=et.y&&this.y+20<=et.y+20){
						return true;
					}
				}
			}
		}
		break;
	}
	return b;
}

//重写run()函数
public void run() {
	while(true){
		switch(this.direct){
		case 0://坦克正在向上移动
			for(int i=0;i<move;i++){
				if(y>0&&!this.isTouchOtherEnemy()){//判断敌人坦克向上走不会越界
					y-=speed;
				}
				try {
					Thread.sleep(50);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			break;
		case 1://向下移动
			for(int i=0;i<move;i++){
				if(y<400&&!this.isTouchOtherEnemy()){//判断敌人坦克向下走不会越界
					y+=speed;
				}
				try {
					Thread.sleep(50);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			break;
		case 2://向左移动
			for(int i=0;i<move;i++){
				if(x>0&&!this.isTouchOtherEnemy()){//判断敌人坦克向左走不会越界
					x-=speed;
				}
				try {
					Thread.sleep(50);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			break;
		case 3://向右移动
			for(int i=0;i<move;i++){
				if(x<300&&!this.isTouchOtherEnemy()){//判断敌人坦克向右走不会越界
					x+=speed;
				}
				try {
					Thread.sleep(50);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			break;
		}
		
		this.times++;
		if(times%2==0){
			if(isLive){
				if(ss.size()<5){
					Shot s=null;
					//没有子弹,添加子弹
					switch(direct){
					case 0:
						s=new Shot(x+10, y, 0);
						ss.add(s);
						break;
					case 1:
						s=new Shot(x+10, y+29, 1);
						ss.add(s);
						break;
					case 2:
						s=new Shot(x, y+10, 2);
						ss.add(s);
						break;
					case 3:
						s=new Shot(x+29, y+10, 3);
						ss.add(s);
						break;
					}
					//启动敌人子弹线程
					Thread t=new Thread(s);
					t.start();
				}
			}
		}
		//让坦克随机产生一个新的行驶方向
		this.direct=(int)(Math.random()*4);
		//判断敌人坦克是否已死亡
		if(this.isLive==false){
			//让坦克死亡后退出线程
			break;
		}
	}
}

}

//我的坦克 class Hero extends Tank{ //子弹 //Shot s=null; Vector ss=new Vector();//Vector线程安全的集合类 Shot s=null;

public Hero(int x,int y){
	super(x,y);
}
//坦克开火
public void shotEnemy(){

	switch(this.direct){//判断坦克方向来对应子弹运行方向
	case 0://向上
		//创建一颗子弹
		s=new Shot(x+10, y, 0);
		//将子弹加入到向量集中
		ss.add(s);
		break;
	case 1://向下
		s=new Shot(x+10, y+29, 1);
		ss.add(s);
		break;
	case 2://向左
		s=new Shot(x, y+10, 2);
		ss.add(s);
		break;
	case 3://向右
		s=new Shot(x+29, y+10, 3);
		ss.add(s);
		break;
	}
	//启动子弹线程
	Thread t=new Thread(s);
	t.start();
}

//坦克向上移动
public void moveUp(){
	y-=speed;
}
//坦克向右移动
public void moveRight(){
	x+=speed;
}
//坦克向下移动
public void moveDown(){
	y+=speed;
}
//坦克向左移动
public void moveLeft(){
	x-=speed;
}

}




#### react和vue的比较

相同
1)vitual dom
2)组件化
3)props,单一数据流

不同点
1)react是jsx和模板;(jsx可以进行更多的js逻辑和操作)
2)状态管理(react)
3)对象属性(vue)
4)vue:view——medol之间双向绑定
5)vue:组件之间的通信(props,callback,emit)

>**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://docs.qq.com/doc/DSmRnRGxvUkxTREhO)**

![](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/a669643c2b5b4413bf1938a95f886fe4~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg55So5oi3MzM5MTQ5MjgwNjA=:q75.awebp?rk3s=f64ab15b&x-expires=1773140848&x-signature=753%2B9se2xA9VOoLVj8kYCFskWn0%3D)

![](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/96f1beebe047482e9760a58824a71f86~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg55So5oi3MzM5MTQ5MjgwNjA=:q75.awebp?rk3s=f64ab15b&x-expires=1773140848&x-signature=PhXfvEOB%2FYrdA%2ByY0uvw7RuvjlE%3D)