Typescript+vite+sass手把手实现五子棋游戏(放置类)

Typescript+vite+sass手把手实现五子棋游戏(放置类)

下面有图片和gif可能没加载出来
image.png

在这里插入图片描述

上面有图片和gif可能没加载出来

导言

最近练习Typescript,觉得差不多了,就用这个项目练练手,使用Typescript纯面向对象编程。

开源地址

试玩地址:试玩地址 (zou-hong-run.github.io)

代码地址:zou-hong-run/dobang: Typescript+vite+sass拖拽放置五子棋 (github.com)

视频演示地址:https://www.bilibili.com/video/BV1JX4y1L7XS/

功能介绍

用户将棋子放置在棋盘上,首先将五颗棋子连成线的用户胜利

游戏功能

  • 开始游戏
  • 用户开始交替放置棋子
  • 放置棋子后该棋子会被禁用,直到对方下子,方可解
  • 五子连成线胜利
  • 重新游戏

项目介绍

使用Typescript+vite+sass构建项目

typescript:类型提示不要太爽。

vite:轻松编译打包项目,减少配置时间

sass:简化css书写

项目搭建

使用vite初始化项目

这里使用vite作为脚手架搭建 因为可以很好的将Typescript和html等结合到一块 打包压缩更方便 支持热更新

你可以使用npm,yarn或pnpm

 npm create vite@latestyarn create vitepnpm create vite

这里我使用的pnpm

 pnpm create vite// 项目名√ Project name: ... gobang// 原生代码,没有框架支持√ Select a framework: » Vanilla// 使用ts√ Select a variant: » TypeScriptcd gobang   pnpm installpnpm run dev

安装sass

方便书写scss,-D装开发依赖

 pnpm add sass -D

项目目录结构

image.png

  • dist

    • 最终打包文件
  • public

    • 图片资源等
  • src

    • 源码入口

    • css

      • 样式
    • script

      • ts代码放置
    • main.ts

      • 代码主入口
  • index.html

    • 网页文件
  • tsconfig.json

    • ts配置文件
  • package.json

    • 包管理文件

前端页面布局

index.html布局

游戏首页index.html

  • #black_piece左边黑子
  • #white_piece右边白子
  • #container_center棋盘
  • #restart 重新游戏
 <!doctype html><html lang="zh">​<head><meta charset="UTF-8" /><link rel="icon" type="image/svg+xml" href="/vite.svg" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Typescript五子棋</title></head>​<body><div id="container"><div id="container_left"><h1>黑棋</h1><button id="black_piece"></button></div><div id="container_center"><div id="title">五子棋对决(等待白棋落子)</div><div id="game"></div></div><div id="container_right"><h1>白棋</h1><button id="white_piece"></button></div><div id="restart" class="none"><button></button></div></div><script type="module" src="./src/main.ts"></script></body>​</html>

sass样式

src/css/style.scss

比原生css简直不要太舒服

 @use "sass:math";​* {padding: 0;margin: 0;box-sizing: border-box;}​html,body {min-width: 660px;min-height: 660px;width: 100%;height: 100%;}​$centerWidthAndHeight: 660px;$leftAndRightWidth: calc((100% - $centerWidthAndHeight)/2);// $centerWidth: 100% - $leftAndRightWidth * 2;// $pieceWidthAndHeight:math.div(100%,1);$pieceWidthAndHeight: 60px;​.none{display: none !important;}#container {width: 100%;height: 100%;display: flex;text-align: center;user-select: none;​h1 {user-select: none;}​&_left,&_right {min-width: 100px;width: $leftAndRightWidth;height: 100%;display: flex;justify-content: center;align-items: center;background-color: #C6BA8A;}// 这里的样式共用#black_piece {width: $pieceWidthAndHeight;height: $pieceWidthAndHeight;background-image: url("../public/imgs/blackPiece.png");background-size: 100% 100%;border-radius: 50%;user-select: all;}​#black_piece:hover {border: 2px double white;}​#white_piece {width: $pieceWidthAndHeight;height: $pieceWidthAndHeight;background-image: url("../public/imgs/whitePiece.png");background-size: 100% 100%;border-radius: 50%;user-select: all;}​#white_piece:hover {border: 2px double black;}​​&_center {width: $centerWidthAndHeight;height: 100%;background-image: url('../public/imgs/background.png');background-repeat: no-repeat;background-size: cover;​#title {background-color: #C6BA8A;// opacity: .9;height: calc(100% - $centerWidthAndHeight);}​#game {// user-select: all;width: $centerWidthAndHeight;height: $centerWidthAndHeight;position: relative;display: flex;flex-wrap: wrap;}}​#restart{width: 100%;height: 100%;position: absolute;display: flex;justify-content: center;align-items: center;background-color: rgba(133, 132, 132,0.5);button{width: 25%;height: 20%;background: url("../../public/imgs/restart.png");background-size:  100% 100%;}}}

工具类封装

src/Utils.ts

此类封装了公用的静态方法

  • clone

    • 克隆元素设置属性
 export default class Utils {static clone(target: HTMLElement,options: Partial<{width: string,height: string,draggable: boolean,userSelect: string,x:string,y:string}>): HTMLElement {let { width, height, draggable, userSelect,x,y } = options;let cloneNode = target.cloneNode(true) as HTMLButtonElement;if (width) {cloneNode.style.cssText += `width:${width};`}if (height) {cloneNode.style.cssText += `height:${height};`;}cloneNode.draggable = draggable as boolean;// 根据父元素的坐标记录该元素的坐标cloneNode.dataset.x = x;cloneNode.dataset.y = y;if (userSelect) {cloneNode.style.cssText += `user-select:${userSelect};`}return cloneNode;​​}​}

游戏逻辑

项目入口

main.ts

  • 导入scss样式
  • 实例化Game类
 import './css/style.scss'import Game from './script/Game'​// 白子优先new Game()

Game类

src/Game.ts 游戏控制类,控制各个类的协调工作

  • 初始参数

  • 创建棋盘

    • new Board
  • 创建黑/白棋子

    • new Piece
  • 等待Board触发的回调函数

    • countPieceCallBack

      • 传入最新棋子数和当前的放在棋盘的棋子
  • 判断胜负

    • isWin

      • 根据isPieceFullFive函数判断是否胜利
    • isPieceFullFive

      • 判断落子点的四周是否五子连续
  • 重新游戏功能

  • 改变标题

 import Board from './Board';import Piece from './Piece';​​export type countPieceCallBack = (count: number, currentPiece: HTMLElement) => voidtype plainArr = ({posX: number;posY: number;name: string;} | {posX: number;posY: number;name: null;})[]​export default class Game {// 标题元素public titleEle:HTMLElement;// 白字优先public firstWhite: boolean;// 棋盘对象public board: Board;// 黑子对象public blackPiece: Piece;// 白子对象public whitePiece: Piece;​// 当前棋盘棋子数量public pieceCount: number;// 当前落子public currentPiece: HTMLElement | undefinedconstructor() {this.titleEle = document.querySelector("#title")!;this.firstWhite = true;// 白子优先this.board = new Board(this.countPieceCallBack.bind(this)); // 初始化棋盘// 初始化白棋子this.blackPiece = new Piece("black_piece", this.firstWhite);// 初始化黑棋子this.whitePiece = new Piece("white_piece", this.firstWhite);// 刚开始为零this.pieceCount = 0;}// 传给Board触发的回调函数countPieceCallBack(count: number, currentPiece: HTMLElement) {// board告诉game棋子数量变化了console.log("board计数", count);// 实时记录最新棋子数量this.pieceCount = count// 交换顺序this.firstWhite = !this.firstWhite;// 通知棋子修改显示状态this.blackPiece.setFirstWhite(this.firstWhite)this.whitePiece.setFirstWhite(this.firstWhite);// 记录当前棋子this.currentPiece = currentPiece// 当前棋子是什么名字let currentPieceName = this.currentPiece?.id;// 改变标题this.changeTitle(currentPieceName);// 判断胜负if(this.isWin()){if(currentPieceName==='black_piece'){alert("黑子获胜!!!");this.changeBackGround(currentPieceName)}else{alert("白子获胜!!!")this.changeBackGround(currentPieceName)}this.addRestartPage()}}addRestartPage(){(document.querySelector("#restart")as HTMLDivElement).classList.remove("none");(document.querySelector("#restart button")as HTMLButtonElement).addEventListener("click",()=>{window.location.reload()})}changeBackGround(currentPieceName:string){let bodycontainer_center = document.querySelector("#container_center") as HTMLDivElementif(currentPieceName==='black_piece'){bodycontainer_center.style.background = `url("../imgs/blackWin.png")`}else{bodycontainer_center.style.background = `url("../imgs/whiteWin.png")`}}changeTitle(currentPieceName:string){this.titleEle.innerText = (currentPieceName==='white_piece'?"(等待黑子落子)-":"(等待白子落子)-")+"总步数:"+this.pieceCount;}// 判断胜负isWin():boolean{// 两种判断,一种全盘判断,一种判断当前落子及其周围是否连成五子// 这里判断当前落子地方及其周围是否连成五子即可if (this.pieceCount >= 8) {let allPiece = this.board.getAllPiece();let dataset = this.currentPiece?.dataset;let { x, y } = dataset!;let currentPieceName = this.currentPiece?.id;let currentPieceposX = parseInt(x!);let currentPieceposY = parseInt(y!);// 提纯allPiecelet plainArr = Array.from(allPiece).map(item => {let children = item.children[0] as HTMLButtonElementif (children) {let name = children.id;let { x, y } = children.dataset;return {posX: parseInt(x!),posY: parseInt(y!),name}}return {posX: parseInt(x!),posY: parseInt(y!),name: null}})// 当前落子的位置let currentPiecePos = {X: currentPieceposX,Y: currentPieceposY,name: currentPieceName!}// 判断是否五子// 竖直方向if(this.isPieceFullFive(currentPiecePos, plainArr,0,1)){return true;}// 横向if(this.isPieceFullFive(currentPiecePos, plainArr,1,0)){return true}// 45度向if(this.isPieceFullFive(currentPiecePos, plainArr,1,1)){return true}// 135度向if(this.isPieceFullFive(currentPiecePos, plainArr,-1,1)){return true}}if (this.pieceCount == 255) {alert("平局");return true;}return false}// 检查从当前位置的竖向,横向,45度向,135度向,的棋子数量是否大于五isPieceFullFive(currentPiecePos: { X: number, Y: number, name: string }, plainArr:plainArr,directX:number,directY:number):boolean {let { X, Y, name } = currentPiecePos;let tempPos = {x:0,y:0};let count = 0;// 从落点位置分为 正方向和反方向// 反方向for(let i=1;i<5;i++){tempPos.x = X - directX*i;tempPos.y = Y - directY*i;if(!plainArr.find(item=>item.name === name&&item.posX === tempPos.x&&item.posY===tempPos.y)){break;}count++;}// 正方向for(let i=1;i<5;i++){tempPos.x = X + directX*i;tempPos.y = Y + directY*i;if(!plainArr.find(item=>item.name === name&&item.posX === tempPos.x&&item.posY===tempPos.y)){break;}count++;}// if(count>=4){// 当前棋子+count=5 游戏胜利return true;}return false}}

Board类

src/Board.ts 棋盘类,控制棋盘格子生成

  • 初始化棋盘参数

  • 初始化棋盘

    • emitGameCountPiece

      • Game传来的回调函数
    • initBoard

      • 创建15*15的棋盘
    • addEventListenerSetGrid

      • 给每个棋盘格子都监听放置事件,棋子放置到网格才触发
    • addEventListenerSetPiece

      • 只要有落子,就会触发该函数
      • 触发Game传来的回调函数emitGameCountPiece
    • getAllPiece

      • 得到棋盘并且包括棋盘中的所有棋子
 import Utils from './Utils'import {type countPieceCallBack} from './Game'export default class Board {// 棋盘行和列private row: number;private col: number;// 网页游戏区域宽高// 游戏区域private game: Element;private gameWidth: number;private gameHeight: number;// 棋盘网格中的单个元素宽高private oneGridWidth: numberprivate oneGridHeight: number// 记录棋盘中的棋子数量public pieceCount: number;// Game传过来的函数,告诉game当前棋盘上的棋子数public emitGameCountPiece: countPieceCallBack;// 记录当前放置的棋子public crrentPiece:HTMLElement|undefined;constructor(emitGameCountPiece:countPieceCallBack) {this.row = 15;this.col = 15;this.game = document.querySelector("#game")!;this.gameWidth = this.game?.clientWidth!this.gameHeight = this.game?.clientHeight!this.oneGridWidth = this.gameWidth / this.rowthis.oneGridHeight = this.gameHeight / this.colthis.pieceCount = 0;this.emitGameCountPiece = emitGameCountPiece;​this.initBoard()}initBoard() {this.initGrid()this.addEventListenerSetPiece()}// 初始化棋盘网格initGrid() {let fragment = document.createDocumentFragment();for (let i = 0; i < this.col; i++) {for (let j = 0; j < this.row; j++) {// 添加网格let grid = document.createElement('div');grid.style.cssText = `border:1px solid black;width:${this.oneGridWidth}px;height:${this.oneGridHeight}px;user-select:none;position:relative;`grid.draggable = false;grid.dataset.x = j + "";grid.dataset.y = i + "";​// 给每个网格监听放置棋子事件this.addEventListenerSetGrid(grid)// 给文档片段添加元素fragment.appendChild(grid);}}this.game.appendChild(fragment)}// 每一个网格都设置一个放置事件addEventListenerSetGrid(ele: Element) {let that = this;// 我们可以看到对于被拖拽元素,事件触发顺序是 dragstart->drag->dragend;// 对于目标元素,事件触发的顺序是 dragenter->dragover->drop/dropleave。ele.addEventListener("dragover", (e) => {// e.stopPropagation()e.preventDefault()});// 防止一个网格放置多个棋子const disableSecondDrop = function () {// 棋盘监听放子会加一,所以这里我们减一that.pieceCount--;// 告诉Game类型that.emitGameCountPiece(that.pieceCount,that.crrentPiece!)alert("此处已经放置元素");return false;}// 一个网格放置一个棋子const drop = function (e: Event) {let parent = ele as HTMLElement;let parentWidth = parent.style.width;let parentHeight = parent.style.height;let x = parent.dataset.x;let y = parent.dataset.y;if (e instanceof DragEvent) {console.log("棋子放置在棋盘上,得到ID");let pieceId = e.dataTransfer?.getData("ID");let pieceEle = document.getElementById(`${pieceId}`)!;// 克隆一个新的棋子let clonePiece = Utils.clone(pieceEle,{width: parentWidth,height: parentHeight, draggable: false, userSelect: "none",x:x,y:y});// 添加到网格中parent.appendChild(clonePiece!)// 记录该棋子的坐标that.crrentPiece = clonePiece;// 禁止该网格放置多个元素parent.addEventListener("drop", disableSecondDrop)// 清除放置事件parent.removeEventListener("drop", drop)}}ele.addEventListener("drop", drop)​}// 监听棋子放置事件addEventListenerSetPiece() {this.game.addEventListener("dragover", (e) => {e.preventDefault()})this.game.addEventListener("drop", () => {console.log("棋盘监听到棋子放下");this.pieceCount++;console.log("棋盘上的棋子数加一", this.pieceCount);// 告诉Game类 数量改变this.emitGameCountPiece(this.pieceCount,this.crrentPiece!)})}// 得到棋盘并且包括棋盘中的所有棋子getAllPiece(){let gameChild = this.game.children;return gameChild;}​}

Piece类

src/Piece.ts 棋子类,控制棋子的各种属性

  • 初始化棋子信息

  • addEventListenerDrag

    • 给黑白棋子添加拖拽事件监听
  • togglePiece

    • 切换黑白棋子放子顺序
  • setFirstWhite

    • 修改当前黑白棋子放子顺序
 ​export default class Piece {private piece: HTMLButtonElement;private firstWhite: boolean;public name: string;constructor(name: string, firstWhite: boolean) {this.name = name;this.firstWhite = firstWhite;this.piece = document.getElementById(`${name}`) as HTMLButtonElement;this.addEventListenerDrag()this.togglePiece()}// 修改当前棋子状态setFirstWhite(value: boolean) {this.firstWhite = value;this.togglePiece()}// 根据isBlack的值禁用左边或者右边棋盘togglePiece() {// 判断当前是黑棋还是白棋let isBlack = this.name === 'black_piece'if (isBlack) {// 黑棋,白棋先手禁用黑棋this.firstWhite ? (this.piece.draggable = false) : (this.piece.draggable = true);this.firstWhite ? (this.piece.disabled = true) : (this.piece.disabled = false);this.firstWhite ? (this.piece.style.opacity = "0.5") : (this.piece.style.opacity = "1");console.log("黑棋先手","draggable:",this.piece.draggable,"disabled:",this.piece.disabled,this.piece.style.opacity);​}else{// 白棋 白棋先手 显示白棋this.firstWhite ? (this.piece.draggable = true) : (this.piece.draggable = false)this.firstWhite ? (this.piece.disabled = false) : (this.piece.disabled = true);this.firstWhite ? (this.piece.style.opacity = "1") : (this.piece.style.opacity = "0.5");console.log("白棋先手","draggable:",this.piece.draggable,"disabled:",this.piece.disabled,this.piece.style.opacity);}​}// 监听器棋子被拖拽addEventListenerDrag() {// 我们可以看到对于被拖拽元素,事件触发顺序是 dragstart->drag->dragend;// 对于目标元素,事件触发的顺序是 dragenter->dragover->drop/dropleave。this.piece.addEventListener("dragstart", (e) => {console.log("棋子开始被拖拽,设置ID");if (e instanceof DragEvent) {e.dataTransfer?.setData("ID", (e.target as Element).id)}});this.piece.addEventListener("drag", (e) => {// e.stopPropagation()e.preventDefault()});this.piece.addEventListener("dragend", (e) => {if (e instanceof DragEvent) {// console.log("棋子被放置");}})}​}

总结

  • 练习本项目,可以提高Typescript使用技巧,理解面向对象知识,提示编码能力
  • 项目还很有多不足,请大家多多指教
  • 大佬们觉得不错的话,请三连支持一下!!!!

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

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

相关文章

马斯克收购AI.com域名巩固xAI公司地位;如何评估大型语言模型的性能

&#x1f989; AI新闻 &#x1f680; AI拍照小程序妙鸭相机上线商业工作站并邀请摄影师进行内测 摘要&#xff1a;AI拍照小程序妙鸭相机将上线面向商业端的工作站&#xff0c;并邀请摄影师进行模板设计的内测。妙鸭相机希望为行业提供更多生态产品&#xff0c;扩大行业规模&a…

在java中如何使用openOffice进行格式转换,word,excel,ppt,pdf互相转换

1.首先需要下载并安装openOffice,下载地址为&#xff1a; Apache OpenOffice download | SourceForge.net 2.安装后&#xff0c;可以测试下是否可用&#xff1b; 3.build.gradle中引入依赖&#xff1a; implementation group: com.artofsolving, name: jodconverter, version:…

安卓4G核心板开发板_MTK6785/MT6785(Helio G95)安卓手机主板方案

联发科MTK6785&#xff08;Helio G95&#xff09;安卓核心板采用八核 CPU 具有两个强大的 Arm Cortex-A76 处理器内核&#xff0c;主频高达 2.05GHz&#xff0c;外加六个 Cortex-A55 高效处理器。其强大的图形性能由 Arm Mali-G76 MC4 提供&#xff0c;速度可提升至 900MHz 。 …

【云原生】K8S二进制搭建二:部署CNI网络组件

目录 一、K8S提供三大接口1.1容器运行时接口CRI1.2云原生网络接口CNI1.3云原生存储接口CSI 二、Flannel网络插件2.1K8S中Pod网络通信2.2Overlay Network2.3VXLAN2.4Flannel 三、Flannel udp 模式的工作原理3.1ETCD 之 Flannel 提供说明 四、vxlan 模式4.1Flannel vxlan 模式的工…

无向图-已知根节点求高度

深搜板子题&#xff0c;无向图&#xff0c;加边加两个&#xff0c;dfs输入两个参数变量&#xff0c;一个是当前深搜节点&#xff0c;另一个是父节点&#xff08;避免重复搜索父节点&#xff09;&#xff0c;恢复现场 ///首先完成数组模拟邻接表#include<iostream> #incl…

记一次 HTTPS 抓包分析和 SNI 的思考

日常听说 HTTPS 是加密协议&#xff0c;那现实中的 HTTPS 流量&#xff0c;是真的完全加密吗&#xff1f; ——答案是&#xff0c;不一定。原因嘛&#xff0c;抓个包就知道了。 我们用 curl 命令触发一下&#xff1a; curl -v https://s-api.37.com.cn/api/xxx * Trying 1…

python 常见数据类型和方法

不可变数据类型 不支持直接增删改 只能查 str 字符串 int 整型 bool 布尔值 None None型特殊常量 tuple 元组(,,,)回到顶部 可变数据类型&#xff0c;支持增删改查 list 列表[,,,] dic 字典{"":"","": ,} set 集合("",""…

Flutter 文件上传(七牛云)简单封装

前言&#xff1a;记录了七牛云上传图片的简单封装、若有不足 欢迎指正。 开始前准备&#xff1a; A、七牛sdk版本一定要和dart版本相对应&#xff08;推荐用any方式、让其自己去匹配&#xff09;&#xff1b; qiniu_flutter_sdk: any B、七牛上传文件所需的参数&#xff1a; …

GPIO实验

一、GPIO GPIO&#xff08;General-purpose input/output&#xff09;即通用型输入输出&#xff0c;GPIO可以控制连接在其之上的引脚实现信号的输入和输出 芯片的引脚与外部设备相连&#xff0c;从而实现与外部硬件设备的通讯、控制及信号采集等功能 LED实验步骤 最终目的&am…

windows 同时安装 Mysql 5.7 和8.0

下载链接 https://dev.mysql.com/downloads/mysql/ 推荐下载 MSI&#xff0c;可以通过图像化界面配置 8.1 版本 安装5.7 系统安装两个MySQL 怎么访问 都是mysql&#xff0c;所以环境变量 配置&#xff0c;只能一个生效&#xff0c;生效就是谁靠前谁生效 cmd 录入 services.m…

Linux用户管理

一、linux用户&#xff1a;username/UID Linux用户分为以下几种 root用户&#xff1a;UID为0&#xff0c;也称超级用户&#xff0c;权限最高。系统用户&#xff1a;UID为1~999&#xff0c;也称虚拟用户、伪用户、假用户&#xff0c;是系统自身拥有的用户&#xff0c;比如bin、…

SpringBoot+SSM实战<一>:打造高效便捷的企业级Java外卖订购系统

文章目录 项目简介项目架构功能模块管理端用户端 技术选型用户层网关层应用层数据层工具 项目优缺点结语 黑马程序员最新Java项目实战《苍穹外卖》&#xff1a;让你轻松掌握SpringBootSSM的企业级开发技巧项目简介 《苍穹外卖》是一款为餐饮企业&#xff08;餐厅、饭店&#x…

C++ 类的继承与派生

1.继承关系举例 交通工具的分类如下图所示&#xff1a; 这个分类树反映了交通工具的派生关系&#xff0c;最高层是抽象程度最高的&#xff0c;是最具有普遍和一般意义的概念&#xff0c;下层具有了上层的特性&#xff0c;同时加入了自己的新特征&#xff0c;而最下层是最为具…

[CKA]考试之PersistentVolumeClaims

由于最新的CKA考试改版&#xff0c;不允许存储书签&#xff0c;本博客致力怎么一步步从官网把答案找到&#xff0c;如何修改把题做对&#xff0c;下面开始我们的 CKA之旅 题目为&#xff1a; Task 创建一个名字为pv-volume的pvc&#xff0c;指定storageClass为csi-hostpath-…

HTML中元素和标签有什么区别?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 标签&#xff08;Tag&#xff09;⭐元素&#xff08;Element&#xff09;⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&a…

Leetcode-每日一题【剑指 Offer 17. 打印从1到最大的n位数】

题目 输入数字 n&#xff0c;按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3&#xff0c;则打印出 1、2、3 一直到最大的 3 位数 999。 示例 1: 输入: n 1输出: [1,2,3,4,5,6,7,8,9] 说明&#xff1a; 用返回一个整数列表来代替打印 n 为正整数 解题思路 前置知识 M…

kubernetes基于helm部署gitlab

kubernetes基于helm部署gitlab 这篇博文介绍如何在 Kubernetes 中使用helm部署 GitLab。 先决条件 已运行的 Kubernetes 集群负载均衡器&#xff0c;为ingress-nginx控制器提供EXTERNAL-IP&#xff0c;本示例使用metallb默认存储类&#xff0c;为gitlab pods提供持久化存储&…

将word每页页眉单独设置

在进行论文排版的时候&#xff0c;总是会出现页眉的页码设置问题&#xff0c;比如出现奇数或偶数页码一致&#xff0c;尝试将前面页码改掉&#xff0c;后面再修改前面也进行了变动&#xff0c;将每页页眉单独设置&#xff1a; &#xff08;1&#xff09;在第一页的最后一行输入…

OPENCV C++(四)形态学操作+连通域统计

形态学操作 先得到一个卷积核 Mat kernel getStructuringElement(MORPH_RECT,Size(5,5)); 第一个是形状 第二个是卷积核大小 依次为腐蚀 膨胀 开运算 闭运算 Mat erodemat,dilatemat,openmat,closemat;morphologyEx(result1, erodemat, MORPH_ERODE, kernel);morphologyEx…

智能指针shared_ptr:自定义删除器

重点&#xff1a; 1.普通指针转化成智能指针。 2.智能指针创建的时候&#xff0c;第二个参数是自定义删除器&#xff0c;默认情况下&#xff0c;shared_ptr调用delete()函数。 class A { public:void Get() { cout << b << endl; }; private:int b{ 10 }; };clas…