package main

import (

    "bufio" // 用到了带缓冲地读取文件数据

    "fmt" // 格式化输出等

    "io" // 判断是否到文件末尾等

    "os" // 只用到了文件打开

    "strconv" // 只用于将string转换为int

    "strings" // 只用到了字符串分割,删除无用字符

)

// 定义一个结构体用于存储稀松矩阵的行列元素值

type ValNode struct {

    row int

    col int

    val int

}

// valNode结构体的构造函数

func newValNode(row, col, val int) ValNode {

    return ValNode{

        row: row,

        col: col,

        val: val,

    }

}

// 存盘

func SaveCheesMap() {

    // 创建原始的二维数组

    // 1代表黑子,2代表白子,0代表没放棋子

    var cheesMap [11][11]int // 初始化一个二维数组代表空棋盘(此处零值=0)

    cheesMap[0][1] = 1

    cheesMap[1][2] = 2

    cheesMap[2][3] = 3

    fmt.Println("输出原始的二维数组")

    for _, v1 := range cheesMap {

        for _, v2 := range v1 {

            fmt.Printf("%d\t", v2)

        }

        fmt.Println() // 遍历完一行换行

    }

    // 转换成稀疏矩阵存盘

    var sparseArr []ValNode // 用来存储稀疏矩阵的所有信息(第一个为稀松矩阵规模:行列数,无效值=0,其他为有效值)

    // 稀松矩阵规模,无效值=0

    valnode0 := newValNode(11, 11, 0)

    sparseArr = append(sparseArr, valnode0)

    // 转换成稀疏矩阵

    for r, v1 := range cheesMap {

        for c, v2 := range v1 {

            if v2 != 0 {

                // 如果不是0,实例化对象 r:行 c:列

                valnode := newValNode(r, c, v2)

                // 添加到sparseArr切片

                sparseArr = append(sparseArr, valnode)

            }

        }

    }

    // 遍历sparseArr存盘

    fileObj, err := os.OpenFile("sparseArr.data", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) // 不存在则创建

    defer fileObj.Close()                                                                    // 即将退出函数是关闭文件

    if err != nil {

        fmt.Println("open file failed,error:", err)

        return

    }

    for _, v := range sparseArr {

        // fmt.Printf("%d %d %d\n", v.row, v.col, v.val)

        fileObj.WriteString(fmt.Sprintf("%d %d %d\n", v.row, v.col, v.val))

    }

}

func ReadFromSparseArr() {

    // 读取之前的存盘并恢复成原始数组(此处使用切片)

    // 1.读盘

    fileObj, err := os.Open("sparseArr.data")

    defer fileObj.Close()

    if err != nil {

        fmt.Println("open file failed,error:", err)

        return

    }

    // 2.按行读取数据并恢复成数组

    var cheesMapNew [][]int

    reader := bufio.NewReader(fileObj)

    times := 0

    for {

        times++

        content, err := reader.ReadString('\n')

        if err == io.EOF {

            // fmt.Println("文件读取完成")

            break

        }

        if err != nil {

            fmt.Println("read file failed,error:", err)

            return

        }

        contentNew := strings.Trim(content, "\n")      // 去掉\n换行符 返回字符串

        contentSlice := strings.Split(contentNew, " ") // 根据空格拆分字符串 返回string的切片

        // 将字符串转换为int

        row, _ := strconv.Atoi(contentSlice[0])

        col, _ := strconv.Atoi(contentSlice[1])

        val, _ := strconv.Atoi(contentSlice[2])

        // 对二维切片进行初始化

        if times == 1 {

            // 遍历到第一个数据(规模,默认值)时对二维切片进行初始化

            // 因为无效数据(没有放棋子)为0,正好等于int切片的零值,因此初始化后不必特殊处理

            cheesMapNew = make([][]int, row) // 共有几行?

            for i, _ := range cheesMapNew {

                cheesMapNew[i] = make([]int, col) // 一行具体有几列:切片初始化

            }

            // 第一个数据:11 11 0只是原来数据的规模,默认值,跳过即可

            continue // 也可以在if语句中else分支写有效数据赋值代码

        }

        // 对初始化好的二维切片赋值(将稀疏矩阵中的有效数据进行还原) 1代表黑子,2代表白子

        cheesMapNew[row][col] = val

    }

    // 验证:输出恢复后的二维序列(切片类型)

    fmt.Println("验证:输出恢复后的二维切片")

    for _, v1 := range cheesMapNew {

        for _, v2 := range v1 {

            fmt.Printf("%d\t", v2)

        }

        fmt.Println()

    }

}

// 程序入口

func main() {

    fmt.Println("苦しんでる人がいるかぎり、頑張ります。Let's Go!")

    // 存盘

    SaveCheesMap()

    // 读取存盘文件并恢复

    ReadFromSparseArr()

}