Python3.4 写的 Nonogram 游戏解题代码

1.关于本文

Nonogram是一种逻辑游戏,以猜谜的方式绘画位图。在一个网格中,每一行和列都有一组数,玩家需根据它们来填满或留空格子,最后就可以由此得出一幅图画。

具体规则可以参见Wikipedia上的相关页面:

中文简体:https://zh.wikipedia.org/zh-cn/Nonogram

英文:https://en.wikipedia.org/wiki/Nonogram

本文中的程序,通过写在文本文件中给定的输入,经过计算,把输出写到另一个文本文件中

(这个程序最开始写出来的目的是用来计算PythonChallenge的第32关的~~~,因此输入文件的格式设计也采用了与该关输入相同的格式)

2.输入的文本文件

输入是一个文本文件。共有Dimensions、Horizontal lines、Vertical lines三个区,Dimensions指定输入矩阵的高度和长度,Horiziontal lines 和 Vertical line 下各行的数字分别代表着Nonogram每行或每列头部出现的提示性数字

如下面这个up.txt,就是一个合法的输入文本:

# Dimensions
32 32

# Horizontal lines
3 2
8
10
3 1 1

5 2 1
5 2 1
4 1 1
15

19
6 14
6 1 12
6 1 10

7 2 1 8
6 1 1 2 1 1 1 1
5 1 4 1
5 4 1 4 1 1 1

5 1 1 8
5 2 1 8
6 1 2 1 3
6 3 2 1

6 1 5
1 6 3
2 7 2
3 3 10 4

9 12 1
22 1
21 4
1 17 1

2 8 5 1
2 2 4
5 2 1 1
5

# Vertical lines
5
5
5
3 1

3 1
5
5
6

5 6
9 5
11 5 1
13 6 1

14 6 1
7 12 1
6 1 11 1
3 1 1 1 9 1

3 4 10
8 1 1 2 8 1
10 1 1 1 7 1
10 4 1 1 7 1

3 2 5 2 1 2 6 2
3 2 4 2 1 1 4 1
2 6 3 1 1 1 1 1
12 3 1 2 1 1 1

3 2 7 3 1 2 1 2
2 6 3 1 1 1 1
12 3 1 5
6 3 1

6 4 1
5 4
4 1 1
5

3.程序代码

FILL, UNCERTAIN, NOTHING = 'X', '_', ' '

def inputdata(inputfile):

    '''
    函数:从inputfile读取数据
    输入:@inputfile,字符串,输入文件
    输出:读取到的数据
          @height 矩阵高度
          @width 矩阵长度
          @horizdata 各行数据(Nonogram矩阵左侧数据)
          @verticdata 各列数据(Nonogram矩阵上侧数据)
    '''

    (height, width, horizdata, verticdata) = 0, 0, [], []

    #标识进入到各个步骤的常量
    STEP_NOTHING = 'NOTHING'
    STEP_READDMI = 'READDIM'
    STEP_READHRZ = 'READHRZ'
    STEP_READVRT = 'READVRT'
    currstep = STEP_NOTHING
    
    #打开文件
    f = open(inputfile, 'r')
    for line in f.readlines():

        #读到空行直接跳过
        if line.strip() == '':
            continue
        
        #去掉行尾的'\n'
        if line[-1] == '\n':
            line = line[:-1]

        #处理读到的数据
        if line.startswith('# Dimensions'):   #读取维度
            currstep = STEP_READDMI
        elif line.startswith('# Horizontal'): #读取矩阵高度
            currstep = STEP_READHRZ
        elif line.startswith('# Vertical'):   #读取矩阵长度
            currstep = STEP_READVRT
        else:                                 #读取数据
            if currstep == STEP_READDMI:      #读取维度
                elements = line.split(' ')
                if elements[0].isdigit() and elements[1].isdigit():
                    height = int(elements[0])
                    width = int(elements[1])
                else:
                    raise Exception("CANNOT READ DIMENSION")
            elif currstep == STEP_READHRZ:    #读取各行数据
                elements = line.split(' ')
                horizdata.append(
                    [int(ele) for ele in elements if ele != '0' and ele.isdigit()])
            elif currstep == STEP_READVRT:    #读取各列数据
                elements = line.split(' ')
                verticdata.append(
                    [int(ele) for ele in elements if ele != '0' and ele.isdigit()])

    #关闭文件   
    f.close()
    
    return (height, width, horizdata, verticdata)


def outputdata(outputfile, matrix):

    '''
    函数:将matrix内容输出到文件outputfile
    输入 @ouputfile:字符串,保存到的文件
    输入 @matrix:一个二维列表
    '''
    
    f = open(outputfile, 'w')
    output = ''
    title = ' |'
    counter = 0
    for row in matrix:
        title += str((counter + 1) % 10)
        output += str((counter + 1) % 10) + '|' + ''.join(row) + '\n'
        counter += 1
    f.write(title + '\n' + '-' * (len(title)) + '\n' + output)
    f.close()


def alternatives(data, length):

    '''
    函数:指出某一行或某一列所有可能的填充方案
    输入 @data:列表
    输入 @length:可填充长度
    输出 @result:列表,可能的填充方案
    '''

    datalen = len(data)
    datasum = sum(data)

    #如果一行或一列不需要任何填充,直接返回唯一的全空备选方案
    if datalen == 0:
        return [UNCERTAIN * length]

    j = 0
    result = []
    if datalen == 1:
        j = 1
    for i in range(length - datalen + 1 - datasum + 1):
        header = UNCERTAIN * i + FILL * data[0] + UNCERTAIN * (1 - j)
        if j:
            tails = [UNCERTAIN * (length - len(header))]
        else:
            tails = alternatives(data[1:], (length - len(header)))
        result.extend([header + tail for tail in tails])
    return result


def decide(alterset, idx):

    '''
    函数:根据某行/列备选组合判断改行/列第idx格是否可判定(一定填充或一定不填充)
    输入 @alterset:备选方案集
    输入 @id:(备选方案的)第idx个格子
    输出 @mark:None:不能确定 FILL:一定填充 NOTHING:一定不填充
    '''

    mark = alterset[0][idx]
    for j in range(1, len(alterset)):
        if mark != alterset[j][idx]:
            return None
    return FILL if mark == FILL else NOTHING


def deleteillegal(alterset, idx, mark):

    '''
    函数:去除非法的候选组合
    输入 @alterset:备选方案集
    输入 @id:(备选方案的)第idx个格子
    输入 @mark:标记,指出该格子一定填充或一定不填充,
                如果备选方案与判定结果不符,则是非法方案,应该删掉
    '''    

    if alterset != 'ELIMINATED' and len(alterset) > 1:
        mark = FILL if mark == FILL else UNCERTAIN
        for i in range(len(alterset) - 1, -1, -1): #删除要从后向前删
            if alterset[i][idx] != mark:
                alterset.pop(i)
            
def drawpic(inputfile, outputfile):

    '''
    函数:绘制题解
    输入 @inputfile:字符串,输入的文件地址
    输入 @outputfile:字符串,输出到的文件地址
    '''
    
    #读取数据
    (height, width, horizdata, verticdata) = inputdata(inputfile)

    #所有可能的候选组合
    alterh = [alternatives(data, width) for data in horizdata]
    alterv = [alternatives(data, height) for data in verticdata]

    counter = 0             #遍历变量
    total = height * width  #矩阵总元素数
    matrix = []             #结果矩阵

    #初始化矩阵
    for i in range(height):
        matrix.append([UNCERTAIN] * width)

    #循环逐行考虑、逐列考虑
    while counter < total:
        
        for i in range(height):                             #逐行考虑
            
            if alterh[i] == 'ELIMINATED':                   #不考虑已经考虑完毕的行
                continue
            if len(alterh[i]) == 0:                         #某行无备选方案,报错
                raise Exception("LACK OF ALTERNATIVES")
            elif len(alterh[i]) == 1:                       #该行只有一种选择的情况
                for j in range(width):                      #逐列筛选可能的组合
                    if matrix[i][j] == UNCERTAIN:           #标记该行剩余未判定信息
                        matrix[i][j] = NOTHING if alterh[i][0][j] != FILL else FILL 
                        counter += 1                        #已判定方格数也要+1
                        deleteillegal(alterv[j], i, matrix[i][j])   
                                                            #删除不可能正确的列组合
                    elif matrix[i][j] != alterh[i][0][j] and alterh[i][0][j] != UNCERTAIN:
                        raise Exception("LACK OF ALTERNATIVES")
                                                            #唯一备选方案与实际不符的情况
                alterh[i] = 'ELIMINATED'                    #该行已经考虑完毕,打上标记
            else:
                for j in range(width):                      #逐列筛选可能的组合
                    if matrix[i][j] != UNCERTAIN:           #只考虑空闲的方格
                        continue
                    mark = decide(alterh[i], j)             #判断第i行第j格是否可判定
                    if mark is not None:                    #该方格可判定的情况
                        matrix[i][j] = mark                 #标记该方格我们判定好的值
                        counter += 1                        #已判定方格数+1
                        deleteillegal(alterv[j], i, mark)   #删除不可能正确的列组合
                        
        for i in range(width):                              #逐列考虑
            
            if alterv[i] == 'ELIMINATED':                   #不考虑已经考虑完毕的列
                continue
            if len(alterv[i]) == 0:                         #某列无备选方案,报错
                raise Exception("LACK OF ALTERNATIVES")
            elif len(alterv[i]) == 1:                       #该列只有一种选择的情况
                for j in range(height):                     #逐行筛选可能的组合
                    if matrix[j][i] == UNCERTAIN:           #标记该列剩余未判定信息
                        matrix[j][i] = NOTHING if alterv[i][0][j] != FILL else FILL
                        counter += 1                        #已判定方格数也要+1
                        deleteillegal(alterh[j], i, matrix[j][i])   
                                                            #删除不可能正确的行组合
                    elif matrix[j][i] != alterv[i][0][j] and alterv[i][0][j] != UNCERTAIN:   
                        raise Exception("LACK OF ALTERNATIVES")
                                                            #唯一备选方案与实际不符的情况
                alterv[i] = 'ELIMINATED'                    #该列已经考虑完毕,打上标记
            else:
                for j in range(height):                     #逐行筛选可能的组合
                    if matrix[j][i] != UNCERTAIN:           #只考虑空闲的方格
                        continue
                    mark = decide(alterv[i], j)             #判断第i列第j格是否可判定
                    if mark is not None:                    #该方格可判定的情况
                        matrix[j][i] = mark                 #标记该方格我们判定好的值
                        counter += 1                        #已判定方格数+1
                        deleteillegal(alterh[j], i, mark)   #删除不可能正确的行组合
    
    #输出数据
    outputdata(outputfile, matrix)

if __name__ == '__main__':
    drawpic('up.txt', 'result.txt')

这段代码调用drawpic函数,从up.txt读入,将最后绘制的结果输出到result.txt中

4.程序输出

result.txt的内容就是上面的输入和代码创造的输出:

140004_dQIE_1425762.png

如果这个图看着不是很直观,那下面还有一张在画图(mspaint)上描格子画出来的图:

140129_VPfB_1425762.png

把眼镜摘了,就能看到图里画的是一条蛇的形状啦 :-P

附:

后来我又在网上找了个在线的Nonogram: http://www.newgrounds.com/portal/view/304506

游戏的名字叫ArmorPicross,玩了一个晚上玩到半夜1:30终于玩通了 ^^,把每关的通关截图上传到网盘,留个档,睡觉! 通关截图地址:http://pan.baidu.com/s/1eQy7vr0

END

转载于:https://my.oschina.net/Tsybius2014/blog/361157

数织图游戏的 Java 代码如下: ``` import java.util.Scanner; public class Nonogram { public static void main(String[] args) { Scanner input = new Scanner(System.in); // 数织图的行列数 System.out.print("请输入数织图的行数:"); int row = input.nextInt(); System.out.print("请输入数织图的列数:"); int col = input.nextInt(); // 定义数织图的行列数组 int[][] rowArr = new int[row][]; int[][] colArr = new int[col][]; for (int i = 0; i < row; i++) { System.out.print("请输入第" + (i + 1) + "行的数字序列,用空格隔开:"); String[] rowStr = input.next().split(" "); rowArr[i] = new int[rowStr.length]; for (int j = 0; j < rowStr.length; j++) { rowArr[i][j] = Integer.parseInt(rowStr[j]); } } for (int i = 0; i < col; i++) { System.out.print("请输入第" + (i + 1) + "列的数字序列,用空格隔开:"); String[] colStr = input.next().split(" "); colArr[i] = new int[colStr.length]; for (int j = 0; j < colStr.length; j++) { colArr[i][j] = Integer.parseInt(colStr[j]); } } // 初始化数织图的二维数组 int[][] nonogram = new int[row][col]; for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { nonogram[i][j] = -1; } } // 打印数织图 printNonogram(nonogram, rowArr, colArr); // 根据玩家的输入更新数织图 while (true) { System.out.print("请输入要填充的行列号和数字(格式为x y z,用空格隔开),输入0结束游戏:"); int x = input.nextInt() - 1; if (x == -1) { break; } int y = input.nextInt() - 1; int z = input.nextInt(); nonogram[x][y] = z; printNonogram(nonogram, rowArr, colArr); } input.close(); } /** * 根据数织图的二维数组和行列数组打印出数织图 */ public static void printNonogram(int[][] nonogram, int[][] rowArr, int[][] colArr) { for (int i = 0; i < rowArr.length; i++) { for (int j = 0; j < rowArr[i].length; j++) { System.out.print(rowArr[i][j] + " "); } System.out.print(" | "); for (int j = 0; j < nonogram[i].length; j++) { System.out.print((nonogram[i][j] == -1) ? " " : (nonogram[i][j] + " ")); } System.out.println(); } for (int i = 0; i < colArr.length; i++) { for (int j = 0; j < colArr[i].length; j++) { System.out.print(colArr[i][j] + " "); } System.out.print(" "); } System.out.println("\n"); } } ``` 在这个代码中,我们使用了 Scanner 类来获取用户的输入。用户首先需要输入数织图的行列数,然后输入每一行和每一列的数字序列。程序会根据这些数字序列来初始化数织图的二维数组。 程序会不断地询问用户要填充的行列号和数字,直到用户输入 0。每次用户输入后,程序会更新数织图的二维数组,并打印出数织图。在打印数织图时,我们还会打印出每一行和每一列的数字序列,方便用户填充数织图。 这个程序只是数织图游戏的基本框架,你可以根据自己的需要对其进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值