用深度强化学习来玩Chrome小恐龙快跑

目录

实机演示

代码实现


实机演示

用深度强化学习来玩Chrome小恐龙快跑

代码实现

import os
import cv2
from pygame import RLEACCEL
from pygame.image import load
from pygame.sprite import Sprite, Group, collide_mask
from pygame import Rect, init, time, display, mixer, transform, Surface
from pygame.surfarray import array3d
import torch
from random import randrange, choice
import numpy as npmixer.pre_init(44100, -16, 2, 2048)
init()scr_size = (width, height) = (600, 150)
FPS = 60
gravity = 0.6black = (0, 0, 0)
white = (255, 255, 255)
background_col = (235, 235, 235)high_score = 0screen = display.set_mode(scr_size)
clock = time.Clock()
display.set_caption("T-Rex Rush")def load_image(name,sizex=-1,sizey=-1,colorkey=None,
):fullname = os.path.join("assets/sprites", name)image = load(fullname)image = image.convert()if colorkey is not None:if colorkey is -1:colorkey = image.get_at((0, 0))image.set_colorkey(colorkey, RLEACCEL)if sizex != -1 or sizey != -1:image = transform.scale(image, (sizex, sizey))return (image, image.get_rect())def load_sprite_sheet(sheetname,nx,ny,scalex=-1,scaley=-1,colorkey=None,
):fullname = os.path.join("assets/sprites", sheetname)sheet = load(fullname)sheet = sheet.convert()sheet_rect = sheet.get_rect()sprites = []sizey = sheet_rect.height / nyif isinstance(nx, int):sizex = sheet_rect.width / nxfor i in range(0, ny):for j in range(0, nx):rect = Rect((j * sizex, i * sizey, sizex, sizey))image = Surface(rect.size)image = image.convert()image.blit(sheet, (0, 0), rect)if colorkey is not None:if colorkey is -1:colorkey = image.get_at((0, 0))image.set_colorkey(colorkey, RLEACCEL)if scalex != -1 or scaley != -1:image = transform.scale(image, (scalex, scaley))sprites.append(image)else:  #listsizex_ls = [sheet_rect.width / i_nx for i_nx in nx]for i in range(0, ny):for i_nx, sizex, i_scalex in zip(nx, sizex_ls, scalex):for j in range(0, i_nx):rect = Rect((j * sizex, i * sizey, sizex, sizey))image = Surface(rect.size)image = image.convert()image.blit(sheet, (0, 0), rect)if colorkey is not None:if colorkey is -1:colorkey = image.get_at((0, 0))image.set_colorkey(colorkey, RLEACCEL)if i_scalex != -1 or scaley != -1:image = transform.scale(image, (i_scalex, scaley))sprites.append(image)sprite_rect = sprites[0].get_rect()return sprites, sprite_rectdef extractDigits(number):if number > -1:digits = []i = 0while (number / 10 != 0):digits.append(number % 10)number = int(number / 10)digits.append(number % 10)for i in range(len(digits), 5):digits.append(0)digits.reverse()return digitsdef pre_processing(image, w=84, h=84):image = image[:300, :, :]# cv2.imwrite("ori.jpg", image)image = cv2.cvtColor(cv2.resize(image, (w, h)), cv2.COLOR_BGR2GRAY)# cv2.imwrite("color.jpg", image)_, image = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)# cv2.imwrite("bw.jpg", image)return image[None, :, :].astype(np.float32)class Dino():def __init__(self, sizex=-1, sizey=-1):self.images, self.rect = load_sprite_sheet("dino.png", 5, 1, sizex, sizey, -1)self.images1, self.rect1 = load_sprite_sheet("dino_ducking.png", 2, 1, 59, sizey, -1)self.rect.bottom = int(0.98 * height)self.rect.left = width / 15self.image = self.images[0]self.index = 0self.counter = 0self.score = 0self.isJumping = Falseself.isDead = Falseself.isDucking = Falseself.isBlinking = Falseself.movement = [0, 0]self.jumpSpeed = 11.5self.stand_pos_width = self.rect.widthself.duck_pos_width = self.rect1.widthdef draw(self):screen.blit(self.image, self.rect)def checkbounds(self):if self.rect.bottom > int(0.98 * height):self.rect.bottom = int(0.98 * height)self.isJumping = Falsedef update(self):if self.isJumping:self.movement[1] = self.movement[1] + gravityif self.isJumping:self.index = 0elif self.isBlinking:if self.index == 0:if self.counter % 400 == 399:self.index = (self.index + 1) % 2else:if self.counter % 20 == 19:self.index = (self.index + 1) % 2elif self.isDucking:if self.counter % 5 == 0:self.index = (self.index + 1) % 2else:if self.counter % 5 == 0:self.index = (self.index + 1) % 2 + 2if self.isDead:self.index = 4if not self.isDucking:self.image = self.images[self.index]self.rect.width = self.stand_pos_widthelse:self.image = self.images1[(self.index) % 2]self.rect.width = self.duck_pos_widthself.rect = self.rect.move(self.movement)self.checkbounds()if not self.isDead and self.counter % 7 == 6 and self.isBlinking == False:self.score += 1self.counter = (self.counter + 1)class Cactus(Sprite):def __init__(self, speed=5, sizex=-1, sizey=-1):Sprite.__init__(self, self.containers)self.images, self.rect = load_sprite_sheet("cacti-small.png", [2, 3, 6], 1, sizex, sizey, -1)self.rect.bottom = int(0.98 * height)self.rect.left = width + self.rect.widthself.image = self.images[randrange(0, 11)]self.movement = [-1 * speed, 0]def draw(self):screen.blit(self.image, self.rect)def update(self):self.rect = self.rect.move(self.movement)if self.rect.right < 0:self.kill()class Ptera(Sprite):def __init__(self, speed=5, sizex=-1, sizey=-1):Sprite.__init__(self, self.containers)self.images, self.rect = load_sprite_sheet("ptera.png", 2, 1, sizex, sizey, -1)self.ptera_height = [height * 0.82, height * 0.75, height * 0.60, height * 0.48]self.rect.centery = self.ptera_height[randrange(0, 4)]self.rect.left = width + self.rect.widthself.image = self.images[0]self.movement = [-1 * speed, 0]self.index = 0self.counter = 0def draw(self):screen.blit(self.image, self.rect)def update(self):if self.counter % 10 == 0:self.index = (self.index + 1) % 2self.image = self.images[self.index]self.rect = self.rect.move(self.movement)self.counter = (self.counter + 1)if self.rect.right < 0:self.kill()class Ground():def __init__(self, speed=-5):self.image, self.rect = load_image("ground.png", -1, -1, -1)self.image1, self.rect1 = load_image("ground.png", -1, -1, -1)self.rect.bottom = heightself.rect1.bottom = heightself.rect1.left = self.rect.rightself.speed = speeddef draw(self):screen.blit(self.image, self.rect)screen.blit(self.image1, self.rect1)def update(self):self.rect.left += self.speedself.rect1.left += self.speedif self.rect.right < 0:self.rect.left = self.rect1.rightif self.rect1.right < 0:self.rect1.left = self.rect.rightclass Cloud(Sprite):def __init__(self, x, y):Sprite.__init__(self, self.containers)self.image, self.rect = load_image("cloud.png", int(90 * 30 / 42), 30, -1)self.speed = 1self.rect.left = xself.rect.top = yself.movement = [-1 * self.speed, 0]def draw(self):screen.blit(self.image, self.rect)def update(self):self.rect = self.rect.move(self.movement)if self.rect.right < 0:self.kill()class Scoreboard():def __init__(self, x=-1, y=-1):self.score = 0self.tempimages, self.temprect = load_sprite_sheet("numbers.png", 12, 1, 11, int(11 * 6 / 5), -1)self.image = Surface((55, int(11 * 6 / 5)))self.rect = self.image.get_rect()if x == -1:self.rect.left = width * 0.89else:self.rect.left = xif y == -1:self.rect.top = height * 0.1else:self.rect.top = ydef draw(self):screen.blit(self.image, self.rect)def update(self, score):score_digits = extractDigits(score)self.image.fill(background_col)if len(score_digits) == 6:score_digits = score_digits[1:]for s in score_digits:self.image.blit(self.tempimages[s], self.temprect)self.temprect.left += self.temprect.widthself.temprect.left = 0class ChromeDino(object):def __init__(self):self.gamespeed = 5self.gameOver = Falseself.gameQuit = Falseself.playerDino = Dino(44, 47)self.new_ground = Ground(-1 * self.gamespeed)self.scb = Scoreboard()self.highsc = Scoreboard(width * 0.78)self.counter = 0self.cacti = Group()self.pteras = Group()self.clouds = Group()self.last_obstacle = Group()Cactus.containers = self.cactiPtera.containers = self.pterasCloud.containers = self.cloudsself.retbutton_image, self.retbutton_rect = load_image("replay_button.png", 35, 31, -1)self.gameover_image, self.gameover_rect = load_image("game_over.png", 190, 11, -1)self.temp_images, self.temp_rect = load_sprite_sheet("numbers.png", 12, 1, 11, int(11 * 6 / 5), -1)self.HI_image = Surface((22, int(11 * 6 / 5)))self.HI_rect = self.HI_image.get_rect()self.HI_image.fill(background_col)self.HI_image.blit(self.temp_images[10], self.temp_rect)self.temp_rect.left += self.temp_rect.widthself.HI_image.blit(self.temp_images[11], self.temp_rect)self.HI_rect.top = height * 0.1self.HI_rect.left = width * 0.73def step(self, action, record=False):  # 0: Do nothing. 1: Jump. 2: Duckreward = 0.1if action == 0:reward += 0.01self.playerDino.isDucking = Falseelif action == 1:self.playerDino.isDucking = Falseif self.playerDino.rect.bottom == int(0.98 * height):self.playerDino.isJumping = Trueself.playerDino.movement[1] = -1 * self.playerDino.jumpSpeedelif action == 2:if not (self.playerDino.isJumping and self.playerDino.isDead) and self.playerDino.rect.bottom == int(0.98 * height):self.playerDino.isDucking = Truefor c in self.cacti:c.movement[0] = -1 * self.gamespeedif collide_mask(self.playerDino, c):self.playerDino.isDead = Truereward = -1breakelse:if c.rect.right < self.playerDino.rect.left < c.rect.right + self.gamespeed + 1:reward = 1breakfor p in self.pteras:p.movement[0] = -1 * self.gamespeedif collide_mask(self.playerDino, p):self.playerDino.isDead = Truereward = -1breakelse:if p.rect.right < self.playerDino.rect.left < p.rect.right + self.gamespeed + 1:reward = 1breakif len(self.cacti) < 2:if len(self.cacti) == 0 and len(self.pteras) == 0:self.last_obstacle.empty()self.last_obstacle.add(Cactus(self.gamespeed, [60, 40, 20], choice([40, 45, 50])))else:for l in self.last_obstacle:if l.rect.right < width * 0.7 and randrange(0, 50) == 10:self.last_obstacle.empty()self.last_obstacle.add(Cactus(self.gamespeed, [60, 40, 20], choice([40, 45, 50])))# if len(self.pteras) == 0 and randrange(0, 200) == 10 and self.counter > 500:if len(self.pteras) == 0 and len(self.cacti) < 2 and randrange(0, 50) == 10 and self.counter > 500:for l in self.last_obstacle:if l.rect.right < width * 0.8:self.last_obstacle.empty()self.last_obstacle.add(Ptera(self.gamespeed, 46, 40))if len(self.clouds) < 5 and randrange(0, 300) == 10:Cloud(width, randrange(height / 5, height / 2))self.playerDino.update()self.cacti.update()self.pteras.update()self.clouds.update()self.new_ground.update()self.scb.update(self.playerDino.score)state = display.get_surface()screen.fill(background_col)self.new_ground.draw()self.clouds.draw(screen)self.scb.draw()self.cacti.draw(screen)self.pteras.draw(screen)self.playerDino.draw()display.update()clock.tick(FPS)if self.playerDino.isDead:self.gameOver = Trueself.counter = (self.counter + 1)if self.gameOver:self.__init__()state = array3d(state)if record:return torch.from_numpy(pre_processing(state)), np.transpose(cv2.cvtColor(state, cv2.COLOR_RGB2BGR), (1, 0, 2)), reward, not (reward > 0)else:return torch.from_numpy(pre_processing(state)), reward, not (reward > 0)
import torch.nn as nnclass DeepQNetwork(nn.Module):def __init__(self):super(DeepQNetwork, self).__init__()self.conv1 = nn.Sequential(nn.Conv2d(4, 32, kernel_size=8, stride=4), nn.ReLU(inplace=True))self.conv2 = nn.Sequential(nn.Conv2d(32, 64, kernel_size=4, stride=2), nn.ReLU(inplace=True))self.conv3 = nn.Sequential(nn.Conv2d(64, 64, kernel_size=3, stride=1), nn.ReLU(inplace=True))self.fc1 = nn.Sequential(nn.Linear(7 * 7 * 64, 512), nn.ReLU(inplace=True))self.fc2 = nn.Linear(512, 3)self._initialize_weights()def _initialize_weights(self):for m in self.modules():if isinstance(m, nn.Conv2d) or isinstance(m, nn.Linear):nn.init.uniform_(m.weight, -0.01, 0.01)nn.init.constant_(m.bias, 0)def forward(self, input):output = self.conv1(input)output = self.conv2(output)output = self.conv3(output)output = output.view(output.size(0), -1)output = self.fc1(output)output = self.fc2(output)return output
import argparse
import torchfrom src.model import DeepQNetwork
from src.env import ChromeDino
import cv2def get_args():parser = argparse.ArgumentParser("""Implementation of Deep Q Network to play Chrome Dino""")parser.add_argument("--saved_path", type=str, default="trained_models")parser.add_argument("--fps", type=int, default=60, help="frames per second")parser.add_argument("--output", type=str, default="output/chrome_dino.mp4", help="the path to output video")args = parser.parse_args()return argsdef q_test(opt):if torch.cuda.is_available():torch.cuda.manual_seed(123)else:torch.manual_seed(123)model = DeepQNetwork()checkpoint_path = "{}/chrome_dino.pth".format(opt.saved_path)checkpoint = torch.load(checkpoint_path)model.load_state_dict(checkpoint["model_state_dict"])model.eval()env = ChromeDino()state, raw_state, _, _ = env.step(0, True)state = torch.cat(tuple(state for _ in range(4)))[None, :, :, :]if torch.cuda.is_available():model.cuda()state = state.cuda()out = cv2.VideoWriter(opt.output, cv2.VideoWriter_fourcc(*"MJPG"), opt.fps, (600, 150))done = Falsewhile not done:prediction = model(state)[0]action = torch.argmax(prediction).item()next_state, raw_next_state, reward, done = env.step(action, True)out.write(raw_next_state)if torch.cuda.is_available():next_state = next_state.cuda()next_state = torch.cat((state[0, 1:, :, :], next_state))[None, :, :, :]state = next_stateif __name__ == "__main__":opt = get_args()q_test(opt)
import argparse
import os
from random import random, randint, sample
import pickle
import numpy as np
import torch
import torch.nn as nnfrom src.model import DeepQNetwork
from src.env import ChromeDinodef get_args():parser = argparse.ArgumentParser("""Implementation of Deep Q Network to play Chrome Dino""")parser.add_argument("--batch_size", type=int, default=64, help="The number of images per batch")parser.add_argument("--optimizer", type=str, choices=["sgd", "adam"], default="adam")parser.add_argument("--lr", type=float, default=1e-4)parser.add_argument("--gamma", type=float, default=0.99)parser.add_argument("--initial_epsilon", type=float, default=0.1)parser.add_argument("--final_epsilon", type=float, default=1e-4)parser.add_argument("--num_decay_iters", type=float, default=2000000)parser.add_argument("--num_iters", type=int, default=2000000)parser.add_argument("--replay_memory_size", type=int, default=50000,help="Number of epoches between testing phases")parser.add_argument("--saved_folder", type=str, default="trained_models")args = parser.parse_args()return argsdef train(opt):if torch.cuda.is_available():torch.cuda.manual_seed(123)else:torch.manual_seed(123)model = DeepQNetwork()if torch.cuda.is_available():model.cuda()optimizer = torch.optim.Adam(model.parameters(), lr=opt.lr)if not os.path.isdir(opt.saved_folder):os.makedirs(opt.saved_folder)checkpoint_path = os.path.join(opt.saved_folder, "chrome_dino.pth")memory_path = os.path.join(opt.saved_folder, "replay_memory.pkl")if os.path.isfile(checkpoint_path):checkpoint = torch.load(checkpoint_path)iter = checkpoint["iter"] + 1model.load_state_dict(checkpoint["model_state_dict"])optimizer.load_state_dict(checkpoint["optimizer"])print("Load trained model from iteration {}".format(iter))else:iter = 0if os.path.isfile(memory_path):with open(memory_path, "rb") as f:replay_memory = pickle.load(f)print("Load replay memory")else:replay_memory = []criterion = nn.MSELoss()env = ChromeDino()state, _, _ = env.step(0)state = torch.cat(tuple(state for _ in range(4)))[None, :, :, :]while iter < opt.num_iters:if torch.cuda.is_available():prediction = model(state.cuda())[0]else:prediction = model(state)[0]# Exploration or exploitationepsilon = opt.final_epsilon + (max(opt.num_decay_iters - iter, 0) * (opt.initial_epsilon - opt.final_epsilon) / opt.num_decay_iters)u = random()random_action = u <= epsilonif random_action:action = randint(0, 2)else:action = torch.argmax(prediction).item()next_state, reward, done = env.step(action)next_state = torch.cat((state[0, 1:, :, :], next_state))[None, :, :, :]replay_memory.append([state, action, reward, next_state, done])if len(replay_memory) > opt.replay_memory_size:del replay_memory[0]batch = sample(replay_memory, min(len(replay_memory), opt.batch_size))state_batch, action_batch, reward_batch, next_state_batch, done_batch = zip(*batch)state_batch = torch.cat(tuple(state for state in state_batch))action_batch = torch.from_numpy(np.array([[1, 0, 0] if action == 0 else [0, 1, 0] if action == 1 else [0, 0, 1] for action inaction_batch], dtype=np.float32))reward_batch = torch.from_numpy(np.array(reward_batch, dtype=np.float32)[:, None])next_state_batch = torch.cat(tuple(state for state in next_state_batch))if torch.cuda.is_available():state_batch = state_batch.cuda()action_batch = action_batch.cuda()reward_batch = reward_batch.cuda()next_state_batch = next_state_batch.cuda()current_prediction_batch = model(state_batch)next_prediction_batch = model(next_state_batch)y_batch = torch.cat(tuple(reward if done else reward + opt.gamma * torch.max(prediction) for reward, done, prediction inzip(reward_batch, done_batch, next_prediction_batch)))q_value = torch.sum(current_prediction_batch * action_batch, dim=1)optimizer.zero_grad()loss = criterion(q_value, y_batch)loss.backward()optimizer.step()state = next_stateiter += 1print("Iteration: {}/{}, Loss: {:.5f}, Epsilon {:.5f}, Reward: {}".format(iter + 1,opt.num_iters,loss,epsilon, reward))if (iter + 1) % 50000 == 0:checkpoint = {"iter": iter,"model_state_dict": model.state_dict(),"optimizer": optimizer.state_dict()}torch.save(checkpoint, checkpoint_path)with open(memory_path, "wb") as f:pickle.dump(replay_memory, f, protocol=pickle.HIGHEST_PROTOCOL)if __name__ == "__main__":opt = get_args()train(opt)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/126320.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

centos7手动配置jdk1.8环境与maven环境

安装jdk1.8 链接&#xff1a;https://pan.baidu.com/s/1_P7jHzH4Lk2jcPWWD7pi4w 提取码&#xff1a;6kkm winscp软件上传压缩包到Linux中 解压 # 解压到/usr/local/java目录下 tar -zxvf jdk-8u381-linux-x64.tar.gz -C /usr/local/java配置环境变量 vi /etc/profile # 最后…

听觉刺激期间的神经血管耦合:ERPs和fNIRS血流动力学

导读 强度依赖性振幅变化(IDAP)已在事件相关电位(ERPs)中进行了广泛的研究&#xff0c;并与多种精神疾病相关联。本研究旨在探讨功能近红外光谱(fNIRS)在IDAP范式中的应用&#xff0c;该范式与ERPs相关&#xff0c;可以指示神经血管耦合的存在。两个实验分别有33和31名参与者。…

【C#】C#调用进程打开一个exe程序

文章目录 一、过程二、效果总结 一、过程 新建WinForm程序&#xff0c;并写入代码&#xff0c;明确要调用的程序的绝对路径&#xff08;或相对路径&#xff09;下的exe文件。 调用代码&#xff1a; 这里我调用的另一个程序的路径是&#xff1a; F:\WindowsFormsApplication2…

MPP 与 SMP 的区别,终于有人讲明白了【文末送书】

文章目录 导读01 SMP1. SMP 的典型特征2. SMP的优缺点 02 分布式MPP计算架构1. MPP 架构核心原理2. MPP 典型特征3. MPP优缺点 写作末尾 导读 当今数据计算领域主要的应用程序和模型可大致分为在线事务处理&#xff08;On-line Transaction Processing &#xff0c;OLTP&#…

如何为虚拟机添加磁盘,扩充原有分区的磁盘空间

如何为虚拟机添加磁盘&#xff0c;扩充原有分区的磁盘空间 关机新增磁盘 虚拟机关机的状态下&#xff0c;在 VMware 当中新增一块磁盘&#xff0c;选中左边要添加磁盘的虚拟机镜像&#xff0c;然后鼠标右键点击设置。 选中磁盘点击添加 点击下一步&#xff0c;悬着SCSI这个…

vue3 封装千分位分隔符自定义指令

toLocaleString作用&#xff1a;在没有指定区域的基本使用时&#xff0c;返回使用默认的语言环境和默认选项格式化的字符串。可点击进入MDN查看 // 千分位分隔符指令 import { Directive, DirectiveBinding } from vueconst thousandSeparator: Directive {mounted(el: any, …

好玩的js特效

记录一些好玩的js特效 1、鱼跳跃特效 引入jquery:https://code.jquery.com/jquery-3.7.1.min.js 源码如下&#xff1a; <!--引入jquery--> <script src"https://code.jquery.com/jquery-3.7.1.min.js"></script> <!--引入跳跃源码--> <s…

深入理解 JVM 之——字节码指令与执行引擎

更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 类文件结构 Write Once&#xff0c;Run Anywhere 对于 C 语言从程序到运行需要经过编译的过程&#xff0c;只有经历了编译后&#xff0c;我们所编写的代码才能够翻译为机器可以直接运行的二进制代码&#x…

230. 二叉搜索树中第K小的元素

230. 二叉搜索树中第K小的元素 C代码&#xff1a;二叉树 int kthSmallest(struct TreeNode* root, int k){// struct TreeNode** stack malloc(sizeof(struct TreeNode*) * 10000); // root 是结构体的地址struct TreeNode* stack[10000];int stkTop 0;while (root ! NULL …

mysql 查询优化 、索引失效

查询优化 物理查询优化 通过索引和表连接方式等技术来进行优化&#xff0c;这里重点需要掌握索引的使用 逻辑查询优化 通过SQL 等价变换 提升查询效率&#xff0c;直白一点就是说&#xff0c;换一种查询写法执行效率可能更高 索引失效 计算、函数、类型转换&#xff08;自动或…

FasterNet(PConv)paper笔记(CVPR2023)

论文&#xff1a;Run, Don’t Walk: Chasing Higher FLOPS for Faster Neural Networks 先熟悉两个概念&#xff1a;FLOPS和FLOPs&#xff08;s一个大写一个小写&#xff09; FLOPS: FLoating point Operations Per Second的缩写&#xff0c;即每秒浮点运算次数&#xff0c;或…

【计算机网络】 确认应答机制与超时重传

文章目录 ACK机制——确认应答机制超时重传 ACK机制——确认应答机制 当我们客户端发送了一个数据&#xff0c;seq是1100&#xff0c;那么服务端在收到时就会回一个ack101的ACK包&#xff0c;代表101之前的包我都收到了&#xff0c;下面请你从101继续发送。然后客户端就会发送1…

Alins - 化繁为简、极致优雅的WebUI框架

最近造了个js框架 Alins&#xff0c;分享一下&#xff1a; &#x1f680; Alins: 最纯粹优雅的WebUI框架 English | 文档 | 演练场 | 更新日志 | 反馈错误/缺漏 | Gitee | 留言板 0 简介 0.1 前言 Alins是一款极致纯粹、简洁、优雅的Web UI框架。秉持0-API、Less is More 的…

慕尼黑主题活动!亚马逊云科技生成式AI全新解决方案,引领未来移动出行领域

IAA作为世界五大车展之一&#xff0c;一直对全球汽车产业的发展起着关键作用&#xff01;2023年9月5日在慕尼黑开幕的IAA MOBILITY 2023以“体验联动智慧出行”为主题&#xff0c;紧跟移动出行领域的前沿变化&#xff0c;将汇集整车企业、开发者、供应商、科技公司、服务提供商…

【小沐学Unity3d】3ds Max 多维子材质编辑(Multi/Sub-object)

文章目录 1、简介2、精简材质编辑器2.1 先创建多维子材质&#xff0c;后指定它2.2 先指定标准材质&#xff0c;后自动创建多维子材质 3、Slate材质编辑器3.1 编辑器简介3.2 编辑器使用 结语 1、简介 多维子材质&#xff08;Multi/Sub-object&#xff09;是为一个模形&#xff0…

tab切换,左右加箭头,点击箭头实现tab切换

和正常tab切换一样原理&#xff0c;点击箭头多了步计算 <template><div><div class"tab-container"><p>{{projectName}}</p><div class"banner"><div v-for"(tab, index) in tabs" :key"index&quo…

物联网世界的无线电报之MQTT详解

文章目录 1. 前言1.1. 物联网与MQTT的关系1.2. MQTT的重要性及应用场景 2. MQTT基础2.1. MQTT的定义与起源2.2. MQTT的工作原理2.3. MQTT的协议格式2.4. 用java造个轮子 3. 深入理解MQTT3.1. MQTT的主要组件3.1.1. Publisher&#xff08;发布者&#xff09;3.1.2. Subscriber&a…

群晖NAS:通过Docker 部署宝塔面板【注册表:cyberbolt/baota】

群晖NAS&#xff1a;通过 Docker 部署宝塔面板【注册表&#xff1a;pch18/baota】 由于 docker 源地址被墙&#xff0c;在面板里面查询不到注册表&#xff0c;使用 ssh 命令行拉取 1、打开 SSH&#xff0c;链接后打开命令行 这里不赘述&#xff0c;具体自行百度 2、下载 镜像…

笔试记录-扔鸡蛋问题

写目录 一个鸡蛋两个鸡蛋K个鸡蛋 今天面试官问了我这个扔鸡蛋问题&#xff0c;以前学过&#xff0c;但是面试的时候想不起来了&#xff0c;应该是直接寄了&#xff0c;接下来总结一下这个问题的动态规划做法. 问题&#xff1a;有一个N层高的楼&#xff0c;现在给你若干个鸡蛋&a…

华为云云耀云服务器L实例评测|使用宝塔面板管理服务器,并搭建个人博客网站

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 公众号&#xff1a;网络豆 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a; 网络豆的主页​​​​​ 目录 前言 介绍&#xff1a; 一.购买使用华为云云耀服务器 …