You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

MIPS汇编开发井字棋:动态数据存储与数组实现技术问询

刚好我之前做过类似的MIPS汇编项目,给你分享两个适配井字棋需求的实用方案,帮你搞定落子记录的存储问题:

方案一:静态预分配数组(最适合井字棋场景)

井字棋最多只有9次落子,完全可以提前在.data段预留足够的固定空间,用一个计数器跟踪当前已记录的落子数,实现简单又高效。

实现思路

  • 定义一个固定大小的数组,每个落子用2字节存储:1字节存玩家标识(比如X的ASCII码88、O的79),1字节存落子位置(0-8,对应9个格子)
  • 用一个变量记录已落子的数量,每次追加时直接计算写入位置,更新计数器即可

代码示例

.data
    move_history: .space 18  # 9步×2字节=18字节,完全够用
    move_count: .word 0       # 记录已保存的落子数量

.text
# 追加落子的函数:a0=玩家标识(X/O的ASCII),a1=落子位置(0-8)
append_move:
    # 计算当前要写入的数组偏移地址
    lw $t0, move_count
    sll $t0, $t0, 1          # 每个落子占2字节,所以计数器×2得到字节偏移
    la $t1, move_history
    add $t1, $t1, $t0        # 最终写入地址

    # 写入玩家标识和位置
    sb $a0, 0($t1)           # 第一个字节存玩家
    sb $a1, 1($t1)           # 第二个字节存位置

    # 更新落子计数器
    lw $t0, move_count
    addi $t0, $t0, 1
    sw $t0, move_count

    jr $ra
方案二:动态堆分配(适合扩展需求)

如果后续要支持多局游戏、保存更多历史记录,可以用MIPS的sbrk系统调用(调用号9)动态分配堆内存,实现可扩容的数组。

实现思路

  • 初始化时分配一块初始大小的堆内存
  • 每次追加落子前检查剩余空间,不足则扩容(通常是翻倍当前大小)
  • 将旧数据复制到新的内存块,再写入新的落子记录

代码示例

.data
    move_history: .word 0     # 存储堆数组的起始地址
    current_size: .word 8     # 初始分配8字节(4个落子的空间)
    move_count: .word 0       # 已保存的落子数量

.text
# 初始化动态数组
init_history:
    li $v0, 9
    lw $a0, current_size
    syscall                  # 分配内存,返回地址存在$v0
    sw $v0, move_history
    jr $ra

# 动态追加落子的函数:a0=玩家标识,a1=落子位置
append_move_dynamic:
    # 检查是否需要扩容
    lw $t0, move_count
    sll $t1, $t0, 1          # 已使用的字节数=落子数×2
    lw $t2, current_size
    bge $t1, $t2, resize_history  # 已用空间≥当前大小,触发扩容

    # 无需扩容,直接写入数据
    lw $t3, move_history
    add $t3, $t3, $t1
    sb $a0, 0($t3)
    sb $a1, 1($t3)
    j update_counter

# 扩容逻辑
resize_history:
    # 把当前内存大小翻倍
    lw $t2, current_size
    sll $t2, $t2, 1
    sw $t2, current_size

    # 分配新的内存块
    li $v0, 9
    move $a0, $t2
    syscall
    move $t4, $v0            # 新内存地址存在$t4

    # 复制旧数据到新内存
    lw $t3, move_history
    move $a1, $t3            # 源地址
    move $a0, $t4            # 目标地址
    move $a2, $t1            # 要复制的字节数
    jal memcpy

    # 更新历史数组的地址
    sw $t4, move_history

    # 写入新的落子记录
    add $t4, $t4, $t1
    sb $a0, 0($t4)
    sb $a1, 1($t4)

# 更新落子计数器
update_counter:
    lw $t0, move_count
    addi $t0, $t0, 1
    sw $t0, move_count
    jr $ra

# 内存复制辅助函数:a0=目标地址,a1=源地址,a2=复制字节数
memcpy:
    beq $a2, $zero, memcpy_done
    lb $t5, 0($a1)
    sb $t5, 0($a0)
    addi $a0, $a0, 1
    addi $a1, $a1, 1
    addi $a2, $a2, -1
    j memcpy
memcpy_done:
    jr $ra

额外小贴士

  • 如果你非要用字符串存储,也可以把每个落子格式化为类似"X0"的字符串片段,然后维护一个带终止符的字符串数组,但这种方式在MIPS里处理起来比直接存二进制数据麻烦很多,不推荐。
  • 静态数组方案完全满足井字棋的单局需求,代码更简洁,运行效率也更高,优先选这个就好。

内容的提问来源于stack exchange,提问作者Felcannon

火山引擎 最新活动