欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用Go語(yǔ)言生成Excel任務(wù)表依賴圖的代碼實(shí)現(xiàn)

 更新時(shí)間:2025年09月09日 10:51:26   作者:witton  
在游戲中,任務(wù)是非常常見(jiàn)的玩法,可能會(huì)有主線任務(wù),支線任務(wù)以及其它一些類型的任務(wù),當(dāng)任務(wù)比較多的時(shí)候,它們的依賴關(guān)系將變得不直觀,很容易出錯(cuò),所以本文介紹了使用Go語(yǔ)言生成Excel任務(wù)表依賴圖的代碼實(shí)現(xiàn),需要的朋友可以參考下

一、前言

在游戲中,任務(wù)是非常常見(jiàn)的玩法,可能會(huì)有主線任務(wù),支線任務(wù)以及其它一些類型的任務(wù),各任務(wù)可能還會(huì)有前置任務(wù),即需要完成某個(gè)任務(wù)之后,才能做當(dāng)前任務(wù)。在游戲開(kāi)發(fā)中,配置表可以使用Excel來(lái)編輯,如果是任務(wù)表,可能會(huì)是如下配置方式:

TaskIDTaskTitlePreTask
10任務(wù)100
20任務(wù)200
11任務(wù)1110
21任務(wù)2120

當(dāng)任務(wù)比較多的時(shí)候,它們的依賴關(guān)系將變得不直觀,很容易出錯(cuò),出錯(cuò)也不容易發(fā)現(xiàn)。

有沒(méi)比較直觀的方式進(jìn)行查看,排錯(cuò)呢?筆者想到了目前非常流程的Markdown文件,它可以簡(jiǎn)單地通過(guò)文本的方式輸入然后輸出強(qiáng)大的各種圖表。這里就可以使用mermaid圖來(lái)直觀展現(xiàn)。

關(guān)于mermaid圖可以去官網(wǎng)查看用例。

注意:mermaid圖在渲染時(shí),如果不設(shè)置subgraph則可能會(huì)出現(xiàn)亂序問(wèn)題,即不是按代碼中出現(xiàn)的順序渲染。

二、實(shí)現(xiàn)

為了方便Go讀取Excel,需要使用相關(guān)的Excel庫(kù),筆者使用excelize庫(kù)。

根據(jù)前面的效果圖,可以知道,這其實(shí)就是一個(gè)深度優(yōu)先的樹(shù),實(shí)現(xiàn)方式有兩種,一種是使用遞歸的方式來(lái)實(shí)現(xiàn),這種方式實(shí)現(xiàn)起來(lái)簡(jiǎn)單,但是如果層次很深,那可能會(huì)出現(xiàn)棧溢出;另一種方式就是使用棧的方式來(lái)實(shí)現(xiàn),將每一層節(jié)點(diǎn)先壓棧,然后從棧頂取出一個(gè)節(jié)點(diǎn)然后再將其所有子節(jié)點(diǎn)入棧,再?gòu)臈m斎〕鲆粋€(gè)節(jié)點(diǎn)處理,依此類推,直到棧中所有節(jié)點(diǎn)處理完畢。

下面列出使用遞歸方式實(shí)現(xiàn)的版本:

/*
MIT License

# Copyright (c) 2023 WittonBell

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package main

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/xuri/excelize/v2"
)

var taskIDField string
var taskTitleField string
var preTaskField string
var noCaseSensitive bool // 是否不區(qū)分大小寫
var fieldNameRow uint    // 字段名所在行號(hào)
var dataStartRow uint    // 數(shù)據(jù)開(kāi)始行號(hào)

type node struct {
	taskID    string
	taskTitle string
}

type multiMap map[string][]*node

func (slf multiMap) Add(key string, nd *node) {
	if len(slf) == 0 {
		slf[key] = []*node{nd}
	} else {
		slf[key] = append(slf[key], nd)
	}
}

func (slf multiMap) Get(key string) []*node {
	if slf == nil {
		return nil
	}
	return slf[key]
}

func (slf multiMap) Del(key string) {
	delete(slf, key)
}

func searchKeyCol(rows *excelize.Rows) (TaskIDCol, PreTaskIDCol, TitleCol int) {
	row, err := rows.Columns()
	if err != nil {
		fmt.Println(err.Error())
	}

	for i, col := range row {
		name := col
		if noCaseSensitive {
			name = strings.ToLower(col)
		}
		if name == preTaskField {
			PreTaskIDCol = i + 1
		} else if name == taskIDField {
			TaskIDCol = i + 1
		} else if name == taskTitleField {
			TitleCol = i + 1
		}
	}
	return
}

func readExcel(filePath string) multiMap {
	fd, err := excelize.OpenFile(filePath)
	if err != nil {
		fmt.Printf("讀取文件`%s`失敗", filePath)
		return nil
	}
	defer func() {
		fd.Close()
	}()
	TaskIDCol, PreTaskIDCol, TitleCol := -1, -1, -1
	sheetName := fd.GetSheetName(0)
	rows, err := fd.Rows(sheetName)
	if err != nil {
		return nil
	}
	defer func() {
		rows.Close()
	}()

	m := multiMap{}
	for i := 1; rows.Next(); i++ {
		if i == int(fieldNameRow) {
			TaskIDCol, PreTaskIDCol, TitleCol = searchKeyCol(rows)
			isOk := true
			if TaskIDCol < 0 {
				isOk = false
				fmt.Printf("要求字段名:%s\n", taskIDField)
			}
			if PreTaskIDCol < 0 {
				isOk = false
				fmt.Printf("要求字段名:%s\n", preTaskField)
			}
			if TitleCol < 0 {
				isOk = false
				fmt.Printf("要求字段名:%s\n", taskTitleField)
			}
			if !isOk {
				return nil
			}
		}
		if i < int(dataStartRow) {
			continue
		}
		TaskIDCell, err := excelize.CoordinatesToCellName(TaskIDCol, i)
		if err != nil {
			continue
		}
		PreTaskIDCell, err := excelize.CoordinatesToCellName(PreTaskIDCol, i)
		if err != nil {
			continue
		}

		TitleColCell, err := excelize.CoordinatesToCellName(TitleCol, i)
		if err != nil {
			continue
		}

		TaskID, err := fd.GetCellValue(sheetName, TaskIDCell)
		if err != nil || TaskID == "" {
			continue
		}

		Title, err := fd.GetCellValue(sheetName, TitleColCell)
		if err != nil || Title == "" {
			continue
		}

		PreTaskID, err := fd.GetCellValue(sheetName, PreTaskIDCell)
		if err != nil {
			continue
		}

		if PreTaskID == "" {
			PreTaskID = "0"
		}

		m.Add(PreTaskID, &node{taskID: TaskID, taskTitle: Title})
	}

	return m
}

func usage() {
	w := flag.CommandLine.Output()
	fmt.Fprintf(w, "%s 應(yīng)用程序是將Excel任務(wù)表中的關(guān)系轉(zhuǎn)換成Markdown的mermaid圖,方便使用Markdown工具直觀地查看任務(wù)依賴。", filepath.Base(os.Args[0]))
	fmt.Fprintln(w)
	fmt.Fprintf(w, "命令格式:%s -hr [字段所在行號(hào)] -dr [數(shù)據(jù)起始行號(hào)] [-nc] -id [任務(wù)ID字段名] -t [任務(wù)標(biāo)題字段名] -pid [前置任務(wù)ID字段名] -o <輸出文件> <Excel文件路徑>", filepath.Base(os.Args[0]))
	fmt.Fprintln(w)
	flag.CommandLine.PrintDefaults()
	fmt.Fprintln(w, "  -h")
	fmt.Fprintln(w, "    \t顯示此幫助")
}

func main() {
	var outputFile string

	flag.CommandLine.Usage = usage
	flag.BoolVar(&noCaseSensitive, "nc", false, "字段名不區(qū)分大小寫")
	flag.UintVar(&fieldNameRow, "hr", 1, "字段所在行號(hào)")
	flag.UintVar(&dataStartRow, "dr", 2, "數(shù)據(jù)起始行號(hào)")
	flag.StringVar(&taskIDField, "id", "ID", "-id [任務(wù)ID字段名]")
	flag.StringVar(&taskTitleField, "t", "Title", "-t [任務(wù)標(biāo)題字段名]")
	flag.StringVar(&preTaskField, "pid", "PreTask", "-pid [前置任務(wù)ID字段名]")
	flag.StringVar(&outputFile, "o", "任務(wù)圖.md", "-o <輸出文件>")

	flag.Parse()
	if flag.NArg() < 1 {
		usage()
		return
	}
	if noCaseSensitive {
		taskIDField = strings.ToLower(taskIDField)
		taskTitleField = strings.ToLower(taskTitleField)
		preTaskField = strings.ToLower(preTaskField)
	}
	mapTask := readExcel(flag.Arg(0))
	buildGraph(mapTask, outputFile)
}

func buildGraph(mapTask multiMap, outputFile string) {
	graph := "```mermaid\ngraph TB\n"
	graph += "subgraph  \n"
	root := mapTask.Get("0")
	for _, v := range root {
		graph += visit(rootNodeName, v, mapTask)
	}
	graph += "end\n"
	graph += "```"

	os.WriteFile(outputFile, []byte(graph), os.ModePerm)

	fmt.Println("完成")
}

func visit(parent string, nd *node, mapTask multiMap) string {
	slice := mapTask.Get(nd.taskID)
	graph := fmt.Sprintf("%s --> %s:%s\n", parent, nd.taskID, nd.taskTitle)
	if parent == rootNodeName {
		graph += "subgraph  \n"
	}
	for _, x := range slice {
		graph += visit(fmt.Sprintf("%s:%s", nd.taskID, nd.taskTitle), x, mapTask)
	}
	mapTask.Del(nd.taskID)
	if parent == rootNodeName {
		graph += "end\n"
	}
	return graph
}

到此這篇關(guān)于使用Go語(yǔ)言生成Excel任務(wù)表依賴圖的代碼實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Go Excel任務(wù)表依賴圖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • golang 各種排序大比拼實(shí)例

    golang 各種排序大比拼實(shí)例

    這篇文章主要介紹了golang 各種排序大比拼實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • 詳解Go如何優(yōu)雅的對(duì)時(shí)間進(jìn)行格式化

    詳解Go如何優(yōu)雅的對(duì)時(shí)間進(jìn)行格式化

    這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言中是如何優(yōu)雅的對(duì)時(shí)間進(jìn)行格式化的,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下
    2023-06-06
  • golang生成指定位數(shù)的隨機(jī)數(shù)的方法

    golang生成指定位數(shù)的隨機(jī)數(shù)的方法

    這篇文章主要介紹了golang生成指定位數(shù)的隨機(jī)數(shù)的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • logrus hook輸出日志到本地磁盤的操作

    logrus hook輸出日志到本地磁盤的操作

    這篇文章主要介紹了logrus hook輸出日志到本地磁盤的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-11-11
  • Golang日志操作庫(kù)zap的使用詳解

    Golang日志操作庫(kù)zap的使用詳解

    zap?是?uber?開(kāi)源的一個(gè)高性能,結(jié)構(gòu)化,分級(jí)記錄的日志記錄包,本文主要為大家詳細(xì)介紹了zap的具體使用,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-03-03
  • golang實(shí)現(xiàn)基于channel的通用連接池詳解

    golang實(shí)現(xiàn)基于channel的通用連接池詳解

    這篇文章主要給大家介紹了關(guān)于golang實(shí)現(xiàn)基于channel的通用連接池的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-02-02
  • Go語(yǔ)言中使用 buffered channel 實(shí)現(xiàn)線程安全的 pool

    Go語(yǔ)言中使用 buffered channel 實(shí)現(xiàn)線程安全的 pool

    這篇文章主要介紹了Go語(yǔ)言中使用 buffered channel 實(shí)現(xiàn)線程安全的 pool,因?yàn)镚o語(yǔ)言自帶的sync.Pool并不是很好用,所以自己實(shí)現(xiàn)了一線程安全的 pool,需要的朋友可以參考下
    2014-10-10
  • Go語(yǔ)言中的range用法實(shí)例分析

    Go語(yǔ)言中的range用法實(shí)例分析

    這篇文章主要介紹了Go語(yǔ)言中的range用法,實(shí)例分析了range的功能與使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • Go操作各大消息隊(duì)列教程(RabbitMQ、Kafka)

    Go操作各大消息隊(duì)列教程(RabbitMQ、Kafka)

    消息隊(duì)列是一種異步的服務(wù)間通信方式,適用于無(wú)服務(wù)器和微服務(wù)架構(gòu),本文主要介紹了Go操作各大消息隊(duì)列教程(RabbitMQ、Kafka),需要的朋友可以了解一下
    2024-02-02
  • 詳解如何在Golang中實(shí)現(xiàn)HMAC

    詳解如何在Golang中實(shí)現(xiàn)HMAC

    HMAC(Hash-based Message Authentication Code)是一種基于 Hash 函數(shù)和密鑰的消息認(rèn)證碼,HMAC將密鑰、消息和哈希函數(shù)一起使用,確保消息在傳輸過(guò)程中不被篡改,還可以驗(yàn)證消息的發(fā)送者身份,本文詳細(xì)講解了如何在Golang中實(shí)現(xiàn)HMAC,需要的朋友可以參考下
    2023-11-11

最新評(píng)論