前言
本来计划换工作,投了简历,人家让做面试题。不管能不能面上,多做点题,总归是好事。
面试题内容如下:
- 题目1
6行7列棋盘, 如下图, 设计一种数据结构,可以表示棋盘?
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
请实现 void print(xxx) 函数,打印棋盘.
介绍:
假如有俩个玩家: player1 黑子,player2 白子
如果棋盘中位置(x,y) 的值 value: 0 表示空, 1 表示黑子, 2 表示白子
给一个M的instance m, 请实现 void print(m) 函数,打印棋盘
case 1> 棋盘全空情况, 创建实例, 然后调用 printM 打印结果如上图
case 2> 初始棋盘所有位置为空(0), 设置 m[0][3] m[3][2] 位置为黑子(1) m[2][3] m[3][5] 为白子(2),
然后调用 print 函数打印棋盘, 打印结果如下图
. . . x . . .
. . . . . . .
. . . o . . .
. . x . . o .
. . . . . . .
. . . . . . .
- 题目2
棋盘6行7列,两个玩家p1, p2 轮流放黑白棋子,
每个玩家 随机抽一列 放入自己的棋子,棋子掉入该列最后空白处,直到填满
(如果该列已经满,玩家此次机会失效,下一个玩家继续)
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
o o . . . . x
o x x x o x o
胜出条件:棋盘被完全填满 (如下图)
x x o x x x o
x o o o x o o
o o x o o o o
x x o x o o x
o o x x x x x
o x o o o x o
- 题目3
胜出条件:
其中一个玩家先出现
1. 一行连续4个子
2. 一列连续4个子
3. 对角线连续4个子
4. 棋盘被完全填满,还未出现胜负,则平局
例如:白子胜出 (第4列)
. . . . . . .
. . . . . . .
. . . o . . .
o x x o x x .
x o x o o o .
x o o o x x .
黑子胜出 (对角线)
. . . . . . .
. . . . . . .
. x . o . . .
x x o x . o x
o o x x . o x
o o o o o x x
平局
o o x o o x o
o o o x o o o
x x x o x x x
o o o x x o x
o o o x o o o
x o o x o o x
写一个程序模拟这个游戏,实现下面要求。
1. 定义Game Class
2. 初始化棋盘后,打印棋盘
3. 不需要human input
4. 游戏结束后,打印棋盘
5. 打印胜出方
开始
这显然是一个五子棋,我的第一反应是用二维数组表示该结构。不过仔细想了想,有没有其他的表达方式?于是就有了链表实现的数据结构。代码如下:
type hand uint
const (
NilHand hand = iota //空白
BlackHand //黑手
WhiteHand //白手
)
func (h hand) Str() string {
switch h {
case NilHand:
return "."
case BlackHand:
return "X"
case WhiteHand:
return "O"
default:
return "."
}
}
type Grid struct {
value hand
left *Grid
right *Grid
top *Grid
bottom *Grid
}
- 首先我们先定义几个函数,方便查找某个点的相邻位置的指针
func (g *Grid) LeftTop() *Grid {
if g.left != nil {
return g.left.top
}
return nil
}
func (g *Grid) LeftBottom() *Grid {
if g.left != nil {
return g.left.bottom
}
return nil
}
func (g *Grid) RightTop() *Grid {
if g.right != nil {
return g.right.top
}
return nil
}
func (g *Grid) RightBottom() *Grid {
if g.right != nil {
return g.right.bottom
}
return nil
}
- 写一个相对偏移的函数Offset()
func (g *Grid) Offset(row, col int) *Grid {
offset := g
if g == nil {
return nil
}
var i, j int
i, j = 1, 1
for i <= row {
if row == i {
for j <= col {
if col == j {
return offset
}
if offset == nil {
return nil
}
offset = offset.right
j++
}
}
if offset == nil {
return nil
}
offset = offset.bottom
i++
}
return nil
}
- 初始化棋盘
func InitGrid(row, col int, head *Grid) *Grid {
l := &Grid{}
l = head
c := col
for c > 1 {
var tmp Grid
tmp.left = l
l.right = &tmp
l = &tmp
c--
}
l = head
r := row
for r > 1 {
var tmp Grid
tmp.top = l
l.bottom = &tmp
l = &tmp
r--
}
for i := 2; i <= row; i++ {
for j := 2; j <= col; j++ {
top := head.Offset(i-1, j)
left := head.Offset(i, j-1)
var tmp Grid
tmp.top = top
tmp.left = left
top.bottom = &tmp
left.right = &tmp
}
}
return head
- 写一个落子动作
func (g *Grid) Set(row, col int, value hand) {
offset := g
offset = g.Offset(row, col)
if offset == nil {
return
} else if offset.value == NilHand {
offset.value = value
}
return
}
- 解题:
- TestGrid()、 Print()实现题目1的要求
- GameOne函数,实现题目2的要求
- GameTwo函数,实现题目3的要求
完整代码
package main
import (
"fmt"
"math/rand"
"strconv"
"strings"
"time"
)
type hand uint
const (
NilHand hand = iota //空白
BlackHand //黑手
WhiteHand //白手
)
func (h hand) Str() string {
switch h {
case NilHand:
return "."
case BlackHand:
return "X"
case WhiteHand:
return "O"
default:
return "."
}
}
type Grid struct {
value hand
left *Grid
right *Grid
top *Grid
bottom *Grid
}
func (g *Grid) LeftTop() *Grid {
if g.left != nil {
return g.left.top
}
return nil
}
func (g *Grid) LeftBottom() *Grid {
if g.left != nil {
return g.left.bottom
}
return nil
}
func (g *Grid) RightTop() *Grid {
if g.right != nil {
return g.right.top
}
return nil
}
func (g *Grid) RightBottom() *Grid {
if g.right != nil {
return g.right.bottom
}
return nil
}
//设置row,col坐标的值, 即 落棋子
func (g *Grid) Set(row, col int, value hand) {
offset := g
offset = g.Offset(row, col)
if offset == nil {
return
} else if offset.value == NilHand {
offset.value = value
}
return
}
//获取向右偏移x位的指针
func (g *Grid) RightOffset(col int) *Grid {
var tmp *Grid
tmp = g
if g == nil {
return nil
}
for i := 1; i <= col; i++ {
if i == col && tmp != nil {
return tmp
}
if tmp == nil {
return nil
}
if tmp.right != nil {
tmp = tmp.right
}
}
return nil
}
//获取向下偏移y位的指针
func (g *Grid) BottomOffset(row int) *Grid {
var tmp *Grid
tmp = g
if g == nil {
return nil
}
for i := 1; i <= row; i++ {
if i == row && tmp != nil {
return tmp
}
if tmp == nil {
return nil
}
if tmp.bottom != nil {
tmp = tmp.bottom
}
}
return nil
}
//获取该表格有几行
func (g *Grid) GetRowLen() int {
var row int
tmp := g
for tmp != nil {
row++
tmp = tmp.bottom
}
return row
}
//获取该表格有几列
func (g *Grid) GetColLen() int {
var col int
tmp := g
for tmp != nil {
col++
tmp = tmp.right
}
return col
}
func (g *Grid) Print() {
fmt.Println("当前棋盘布局为:")
var colNumStr = ""
col := g.GetColLen()
fillC := strconv.Itoa(g.GetColLen())
for i := 1; i <= col; i++ {
colNumStr += " " + StrLeftFill(len(fillC), i)
}
fmt.Println(StrLeftFill(len(strconv.Itoa(g.GetRowLen())), ""), strings.TrimLeft(colNumStr, " "))
for row := 1; row <= g.GetRowLen(); row++ {
var rowStr = ""
for col := 1; col <= g.GetColLen(); col++ {
//rowStr += StrLeftFill(len(strconv.Itoa(g.GetColLen())), "") + g.Offset(row, col).value.Str()
rowStr += " " + StrLeftFill(len(strconv.Itoa(g.GetColLen())), g.Offset(row, col).value.Str())
}
fmt.Println(StrLeftFill(len(strconv.Itoa(g.GetRowLen())), row), strings.TrimLeft(rowStr, " "))
}
fmt.Println("")
}
/*
获取坐标处的指针
*/
func (g *Grid) Offset(row, col int) *Grid {
offset := g
if g == nil {
return nil
}
var i, j int
i, j = 1, 1
for i <= row {
if row == i {
for j <= col {
if col == j {
return offset
}
if offset == nil {
return nil
}
offset = offset.right
j++
}
}
if offset == nil {
return nil
}
offset = offset.bottom
i++
}
return nil
}
//获取某列最后一行的棋子的指针
func (g *Grid) GetLastRow(col int) *Grid {
offset := g.Offset(1, col)
for row := 1; row <= g.GetRowLen(); row++ {
if row == g.GetRowLen() {
return offset
}
offset = offset.bottom
}
return nil
}
//获取某一行的最后一列没有落棋子的指针
func (g *Grid) GetEmptyLastRow(col int) *Grid {
if col <= 0 || col > g.GetColLen() {
return nil
}
last := g.GetLastRow(col)
if last == nil {
return nil
}
for row := 1; row <= g.GetRowLen(); row++ {
if last.value == NilHand {
return last
}
last = last.top
if last == nil {
return nil
}
}
return nil
}
//棋盘是否已满?
func (g *Grid) IsFull() bool {
for row := 1; row <= g.GetRowLen(); row++ {
for col := 1; col <= g.GetColLen(); col++ {
if g.Offset(row, col).value == NilHand {
return false
}
}
}
return true
}
//统计黑手,白手的棋子数量
func (g *Grid) Count() (black, white int) {
for row := 1; row <= g.GetRowLen(); row++ {
for col := 1; col <= g.GetColLen(); col++ {
if g.Offset(row, col).value == BlackHand {
black++
} else if g.Offset(row, col).value == WhiteHand {
white++
}
}
}
return
}
//检查是否已分出胜负
func (g *Grid) IsWin(row, col int) bool {
offset := g.Offset(row, col)
h := offset.value
if h == NilHand {
return false
}
//检查行
count := 1
left := offset.left
right := offset.right
for {
if left == nil {
break
}
if left.value == h {
count++
} else {
break
}
left = left.left
}
for {
if right == nil {
break
}
if right.value == h {
count++
} else {
break
}
right = right.right
}
if count >= 4 {
switch h {
case WhiteHand:
fmt.Println("白手赢", fmt.Sprintf("最后一个落子点为(row:%d,col:%d)", row, col))
case BlackHand:
fmt.Println("黑手赢", fmt.Sprintf("最后一个落子点为(row:%d,col:%d)", row, col))
}
return true
}
//检查列
count = 1
top := offset.top
bottom := offset.bottom
for {
if top == nil {
break
}
if top.value == h {
count++
} else {
break
}
top = top.top
}
for {
if bottom == nil {
break
}
if bottom.value == h {
count++
} else {
break
}
bottom = bottom.bottom
}
if count >= 4 {
switch h {
case WhiteHand:
fmt.Println("白手赢", fmt.Sprintf("最后一个落子点为(row:%d,col:%d)", row, col))
case BlackHand:
fmt.Println("黑手赢", fmt.Sprintf("最后一个落子点为(row:%d,col:%d)", row, col))
}
return true
}
//检查左斜边
count = 1
leftTop := offset.LeftTop()
rightBottom := offset.RightBottom()
for {
if leftTop == nil {
break
}
if leftTop.value == h {
count++
} else {
break
}
leftTop = leftTop.LeftTop()
}
for {
if rightBottom == nil {
break
}
if rightBottom.value == h {
count++
} else {
break
}
rightBottom = rightBottom.RightBottom()
}
if count >= 4 {
switch h {
case WhiteHand:
fmt.Println("白手赢", fmt.Sprintf("最后一个落子点为(row:%d,col:%d)", row, col))
case BlackHand:
fmt.Println("黑手赢", fmt.Sprintf("最后一个落子点为(row:%d,col:%d)", row, col))
}
return true
}
//检查右斜边
count = 1
rightTop := offset.RightTop()
leftBottom := offset.LeftBottom()
for {
if rightTop == nil {
break
}
if rightTop.value == h {
count++
} else {
break
}
rightTop = rightTop.RightTop()
}
for {
if leftBottom == nil {
break
}
if leftBottom.value == h {
count++
} else {
break
}
leftBottom = leftBottom.LeftBottom()
}
if count >= 4 {
switch h {
case WhiteHand:
fmt.Println("白手赢", fmt.Sprintf("最后一个落子点为(row:%d,col:%d)", row, col))
case BlackHand:
fmt.Println("黑手赢", fmt.Sprintf("最后一个落子点为(row:%d,col:%d)", row, col))
}
return true
}
if g.IsFull() {
fmt.Println("平手")
return true
} else {
return false
}
}
/*
约定: row,col的起始值为1
*/
func InitGrid(row, col int, head *Grid) *Grid {
l := &Grid{}
l = head
c := col
for c > 1 {
var tmp Grid
tmp.left = l
l.right = &tmp
l = &tmp
c--
}
l = head
r := row
for r > 1 {
var tmp Grid
tmp.top = l
l.bottom = &tmp
l = &tmp
r--
}
for i := 2; i <= row; i++ {
for j := 2; j <= col; j++ {
top := head.Offset(i-1, j)
left := head.Offset(i, j-1)
var tmp Grid
tmp.top = top
tmp.left = left
top.bottom = &tmp
left.right = &tmp
}
}
return head
}
func TestGrid(row, col int) {
/*
简单的测试下棋子
设置:
(1,4),(4,3)为黑手
(3,4),(4,6)为白手
*/
grid := InitGrid(row, col, &Grid{})
grid.Set(1, 4, BlackHand)
grid.Set(4, 3, BlackHand)
grid.Set(3, 4, WhiteHand)
grid.Set(4, 6, WhiteHand)
grid.Print()
}
func GameOne(row, col int) {
/*
游戏规则:
两个玩家p1,p2轮流放黑白棋子,每个玩家随机抽取一列放入自己的棋子,棋子调入该列最后空白处(可理解为压栈),直到填满.
(如果该列已经满了,玩家此次机会失效,下一个玩家继续).
胜出条件:
棋盘被完全占满(最多的为胜?)
*/
grid := InitGrid(row, col, &Grid{})
rand.Seed(time.Now().Unix())
i := 0 //
for {
if grid.IsFull() {
black, white := grid.Count()
if black > white {
fmt.Println("黑手胜", fmt.Sprintf("黑手:%d;白手:%d", black, white))
} else if black < white {
fmt.Println("白手胜", fmt.Sprintf("黑手:%d;白手:%d", black, white))
} else {
fmt.Println("平手", fmt.Sprintf("黑手:%d;白手:%d", black, white))
}
grid.Print()
break
}
//规定偶数为: 黑手
h := grid.GetEmptyLastRow(rand.Intn(grid.GetColLen()) + 1)
if h == nil {
} else if i%2 == 0 {
h.value = BlackHand
} else {
h.value = WhiteHand
}
i++
}
}
//落子的坐标
type XY struct {
row int
col int
}
func GameTwo(row, col int) {
/*
游戏规则:(和我们平时玩的规则一样)
1. 一行连续4个子
2. 一列连续4个子
3. 对角线连续4个子
4. 棋盘被完全填满,还未出现胜负,则平局
*/
grid := InitGrid(row, col, &Grid{})
//生成所有的棋子位置
xy := map[int]XY{}
var loop int // 第几次循环
for r := 1; r <= row; r++ {
for c := 1; c <= col; c++ {
xy[loop] = XY{row: r, col: c}
loop++
}
}
rand.Seed(time.Now().Unix())
p := XY{1, 1}
i := 0
for {
//if grid.IsFull() {
// break
//}
if grid.IsWin(p.row, p.col) {
grid.Print()
break
}
//随机落棋
for {
if v, ok := xy[rand.Intn(loop+1)]; ok {
p = v
delete(xy, loop) //已取出,删除该坐标
break
}
//棋子坐标非法, continue
}
if i%2 == 0 {
//黑手落棋子
grid.Set(p.row, p.col, BlackHand)
} else {
//白手落棋子
grid.Set(p.row, p.col, WhiteHand)
}
i++
}
}
//字符串左边填充
func StrLeftFill(s int, value interface{}) string {
var format = ""
format = "%" + strconv.Itoa(s) + "v"
return fmt.Sprintf(format, value)
}
func main() {
grid := InitGrid(6, 7, &Grid{})
//空棋盘
grid.Print()
TestGrid(6, 7)
GameOne(6, 7)
GameTwo(20, 20)
}
运行结果
当前棋盘布局为:
1 2 3 4 5 6 7
1 . . . . . . .
2 . . . . . . .
3 . . . . . . .
4 . . . . . . .
5 . . . . . . .
6 . . . . . . .
当前棋盘布局为:
1 2 3 4 5 6 7
1 . . . X . . .
2 . . . . . . .
3 . . . O . . .
4 . . X . . O .
5 . . . . . . .
6 . . . . . . .
黑手胜 黑手:22;白手:20
当前棋盘布局为:
1 2 3 4 5 6 7
1 O O O X X O X
2 O O O X X X X
3 X O O O X X X
4 O X X X X O O
5 O O X X X X O
6 X O O O O X X
白手赢 最后一个落子点为(row:19,col:8)
当前棋盘布局为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
1 . X . . . . . . . X . . . . . . O . X X
2 . . . . X X . X O . . . O . . . X . X .
3 . . . . . . . . X . . . . . O . X X . X
4 . . O X . . . O . X O X O . . . . . . .
5 . . . . . . X . O . . O . . . . . X X .
6 . O O . O O . . . . . O . X O X . . . O
7 . . . . X . . . . . . . . . . . . . . .
8 X . . X . O . . . . . X X . . . O X O O
9 O O . O . O . . O . X X . . . O . . . .
10 O . . X X . O . O . . X X . . X . . . X
11 . . . . . O . X O . O . O . . . . . . .
12 . O X X . X X . . . . . . X . . X O . .
13 X . X X . . . . . . . X . . O O . . . .
14 . . . . . X . . . X . X . . . . . . . X
15 . . . O . . . . . . O O . . . . . X . X
16 . O . O O X . O O O . . . . . . . O . .
17 . . O O X . . O . . X X . X . . . O O X
18 . . . X . . . O X . X . . . O O . X . X
19 . . . . O O . O O . . O . . O . . . . O
20 . X . . X X . X . . . . . . . . O O O .