Python游戲開發(fā):數(shù)字華容道

今年年初稠屠,新一季的《最強(qiáng)大腦》開播了,第一集選拔的時(shí)候大家做了一個(gè)數(shù)字游戲翎苫,名叫《數(shù)字華容道》权埠,當(dāng)時(shí)何猷君以二十幾秒的成績(jī)奪得該項(xiàng)目的冠軍,看了這個(gè)游戲之后我決定要寫一個(gè)《數(shù)字華容道》的程序煎谍,過(guò)去了半年攘蔽,我終于記起了這件事,今天就來(lái)實(shí)現(xiàn)呐粘。

數(shù)字推盤游戲(n-puzzle)是一種智力游戲满俗,常見的類型有十五數(shù)字推盤游戲和八數(shù)字推盤游戲等。十五數(shù)字推盤游戲的板上會(huì)有十五個(gè)方塊和一個(gè)大小相當(dāng)于一個(gè)方塊的空位(供方塊移動(dòng)之用)作岖,當(dāng)15個(gè)數(shù)字依次排序并且最后一個(gè)格子為空位即代表挑戰(zhàn)成功唆垃。

本文使用 PyQt5 進(jìn)行設(shè)計(jì)與實(shí)現(xiàn),PyQt5 是該程序的一個(gè)呈現(xiàn)方式痘儡,最重要的是算法降盹,學(xué)會(huì)了算法,完全可以使用 PyGame 或者 Tkinter 實(shí)現(xiàn)。

PyQt5安裝:pip install PyQt5

本文使用環(huán)境:

  • 系統(tǒng):Windows 10 64位
  • Python版本:3.6

1蓄坏、布局設(shè)計(jì)

做一個(gè)簡(jiǎn)版的數(shù)字華容道价捧,布局設(shè)計(jì)如圖所示:

簡(jiǎn)版數(shù)字華容道布局
  • 圖中灰色的部分使用 QWidget 作為整個(gè)游戲的載體;
  • 黃色部分使用 QGridLayout 作為數(shù)字方塊的布局涡戳;
  • 紅色部分使用 QLabel 作為數(shù)字方塊结蟋。

2、算法設(shè)計(jì)

如上圖所示渔彰,本游戲共需要15個(gè)方塊嵌屎,每個(gè)方塊代表一個(gè)數(shù)字。我們可以使用 一個(gè)二維 list 來(lái)存儲(chǔ)方塊上的數(shù)字恍涂。其實(shí)我們要?jiǎng)?chuàng)建一個(gè) 4x4 的 list 存儲(chǔ) 0~15 各個(gè)數(shù)字宝惰,0 代表空的位置。

2.1 創(chuàng)建并初始化數(shù)組

創(chuàng)建數(shù)組的方法:

  • 創(chuàng)建一個(gè)長(zhǎng)度為16的數(shù)組再沧,并且在對(duì)應(yīng)位置上保存著 0~15 尼夺;
  • 打亂順序
import random

# 用來(lái)存放位置信息的二維數(shù)組
blocks = []

# 產(chǎn)生隨機(jī)數(shù)組,0 代表空的位置
arr = range(16)
numbers = random.sample(arr, 16)

for row in range(4):
    blocks.append([])
    for column in range(4):
        blocks[row].append(numbers[row*4 + column])

# 打印結(jié)果
for i in range(4):
    print(blocks[i])

[out]
[2, 5, 7, 9]
[11, 8, 4, 12]
[6, 13, 10, 15]
[1, 14, 0, 3]
[Finished in 0.1s]

2.2 移動(dòng)算法

假如移動(dòng)之前個(gè)數(shù)字位置如左圖所示,那么當(dāng)按下左箭頭時(shí)炒瘸,會(huì)變成如右圖所示:

左移

可以看到 (1, 2)(1, 3) 兩個(gè)位置上的數(shù)字互換了淤堵,即 0 和 8 互換;如果右圖所示再次按下左箭頭顷扩,那么所有數(shù)字都不會(huì)改變拐邪,因?yàn)?數(shù)字 0 右邊沒(méi)有數(shù)了。

總結(jié)一下:如果 數(shù)字 0 所在位置為 (row, column)隘截,并且 column≠3 那么按下左箭頭之后扎阶,(row, column) 和 (row, column+1) 位置上的數(shù)組互換,同理可得:

  • 如果 數(shù)字 0 所在位置為 (row, column)婶芭,并且 column≠0 那么按下右箭頭之后乘陪,(row, column) 和 (row, column-1) 位置上的數(shù)組互換;
  • 如果 數(shù)字 0 所在位置為 (row, column)雕擂,并且 row≠3 那么按下上箭頭之后,(row, column) 和 (row+1, column) 位置上的數(shù)組互換贱勃;
  • 如果 數(shù)字 0 所在位置為 (row, column)井赌,并且 row≠0 那么按下下箭頭之后,(row, column) 和 (row-1, column) 位置上的數(shù)組互換贵扰;

將移動(dòng)算法封裝成一個(gè)函數(shù)如下:

# 移動(dòng)
# zero_row 代表數(shù)字0 所在二維數(shù)組的行下標(biāo)仇穗,zero_column代表數(shù)字0 所在二維數(shù)組的列下標(biāo)
def move(direction):
    if(direction == 'UP'): # 上
        if zero_row != 3:
            blocks[zero_row][zero_column] = blocks[zero_row + 1][zero_column]
            blocks[zero_row + 1][zero_column] = 0
            zero_row += 1
    if(direction == 'DOWN'): # 下
        if zero_row != 0:
            blocks[zero_row][zero_column] = blocks[zero_row - 1][zero_column]
            blocks[zero_row - 1][zero_column] = 0
            zero_row -= 1
    if(direction == 'LEFT'): # 左
        if zero_column != 3:
            blocks[zero_row][zero_column] = blocks[zero_row][zero_column + 1]
            blocks[zero_row][zero_column + 1] = 0
            zero_column += 1
    if(direction == 'RIGHT'): # 右
        if zero_column != 0:
            blocks[zero_row][zero_column] = blocks[zero_row][zero_column - 1]
            blocks[zero_row][zero_column - 1] = 0
            zero_column -= 1

2.3 是否勝利檢測(cè)算法

檢測(cè)是否勝利其實(shí)很簡(jiǎn)單:前15個(gè)位置分別對(duì)應(yīng),最后一個(gè)為0即為勝利 戚绕,不過(guò)為了避免不必要的計(jì)算纹坐,我們先檢測(cè)最后一個(gè)是否為 0 ,如果不為0 前面的就不用比較了舞丛。具體代碼實(shí)現(xiàn)如下:

# 檢測(cè)是否完成
def checkResult():
        # 先檢測(cè)最右下角是否為0
        if blocks[3][3] != 0:
            return False

        for row in range(4):
            for column in range(4):
                # 運(yùn)行到此處說(shuō)名最右下角已經(jīng)為0耘子,pass即可
                if row == 3 and column == 3:
                    pass
                # 值是否對(duì)應(yīng)
                elif blocks[row][column] != row * 4 + column + 1:
                    return False

        return True

3果漾、實(shí)現(xiàn)

下面講解所有功能模塊的實(shí)現(xiàn)。

3.1 框架搭建

創(chuàng)建 QWidget 作為整個(gè)游戲的載體:

import sys
from PyQt5.QtWidgets import QWidget, QApplication

class NumberHuaRong(QWidget):
    """ 華容道主體 """
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # 設(shè)置寬和高
        self.setFixedSize(400, 400)
        # 設(shè)置標(biāo)題
        self.setWindowTitle('數(shù)字華容道')
        # 設(shè)置背景顏色
        self.setStyleSheet("background-color:gray;")
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = NumberHuaRong()
    sys.exit(app.exec_())

運(yùn)行結(jié)果如下圖所示:

框架運(yùn)行結(jié)果

3.2 數(shù)字方塊實(shí)現(xiàn)

前面已經(jīng)提到谷誓,用一個(gè)二維數(shù)組來(lái)存放 0~16 個(gè)數(shù)字,最終我們要轉(zhuǎn)換成一個(gè)數(shù)字方塊捍歪,單獨(dú)創(chuàng)建一個(gè)類:

class Block(QLabel):
    """ 數(shù)字方塊 """
    def __init__(self, number):
        super().__init__()

        self.number = number
        self.setFixedSize(80, 80)

        # 設(shè)置字體
        font = QFont()
        font.setPointSize(30)
        font.setBold(True)
        self.setFont(font)

        # 設(shè)置字體顏色
        pa = QPalette()
        pa.setColor(QPalette.WindowText, Qt.white)
        self.setPalette(pa)

        # 設(shè)置文字位置
        self.setAlignment(Qt.AlignCenter)

        # 設(shè)置背景顏色\圓角和文本內(nèi)容
        if self.number == 0:
            self.setStyleSheet("background-color:white;border-radius:10px;")
        else:
            self.setStyleSheet("background-color:red;border-radius:10px;")
            self.setText(str(self.number))

該類繼承自 QLablel ,初始化需要傳入一個(gè)參數(shù) number 糙臼,number就是數(shù)字方塊上顯示的數(shù)字庐镐。

3.3 將數(shù)字轉(zhuǎn)換成方塊添加到布局

布局采用 QGridLayout 創(chuàng)建一個(gè) 4X4 的 self.gltMain必逆,將16個(gè) Block 添加到 self.gltMain:

def updatePanel(self):
    for row in range(4):
        for column in range(4):
            self.gltMain.addWidget(Block(self.blocks[row][column]), row, column)

    self.setLayout(self.gltMain)

3.4 初始化布局

初始化布局包括隨機(jī)數(shù)據(jù)的產(chǎn)生與將數(shù)字轉(zhuǎn)換成方塊添加到布局

# 初始化布局
    def onInit(self):
        # 產(chǎn)生隨機(jī)數(shù)組,0 代表空的位置
        arr = range(16)
        self.numbers = random.sample(arr, 16)

        # 將數(shù)字方塊添加到布局
        for row in range(4):
            self.blocks.append([])
            for column in range(4):
                temp = self.numbers[row * 4 + column]

                if temp == 0:
                    self.zero_row = row
                    self.zero_column = column
                self.blocks[row].append(temp)
                self.gltMain.addWidget(Block(temp), row, column)

3.5 按鍵檢測(cè)

QWidget 有一個(gè) keyPressEvent 事件句柄,我們只需要重新實(shí)現(xiàn)該方法即可:

# 檢測(cè)按鍵
def keyPressEvent(self, event):
    key = event.key()
    if(key == Qt.Key_Up or key == Qt.Key_W):
        self.move(Direction.UP)
    if(key == Qt.Key_Down or key == Qt.Key_S):
        self.move(Direction.DOWN)
    if(key == Qt.Key_Left or key == Qt.Key_A):
        self.move(Direction.LEFT)
    if(key == Qt.Key_Right or key == Qt.Key_D):
        self.move(Direction.RIGHT)
    self.updatePanel()
    if self.checkResult():
        if QMessageBox.Ok == QMessageBox.information(self, '挑戰(zhàn)結(jié)果', '恭喜您完成挑戰(zhàn)末患!'):
            self.onInit()

按鍵檢測(cè)到按鍵按下之后判斷該鍵值是否為 “↑↓←→”或“WSAD”,并作出相應(yīng)的移動(dòng)(move)锤窑,移動(dòng)之后刷新布局(updatePannel),最后檢測(cè)是否完成挑戰(zhàn)(checkResult)渊啰,如果完成挑戰(zhàn),彈出提示框绘证。如果點(diǎn)擊了 OK 按鈕隧膏,游戲重新開始(onInit)嚷那。

3.6 試玩測(cè)試

至此,所有功能模塊介紹完畢魏宽,不要著急看完整代碼,我們先運(yùn)行一下程序看是否還有 Bugs队询。

玩了幾局之后發(fā)現(xiàn)派桩,并不是所有的局都能都還原,如下面這種情況:

無(wú)法還原

如圖所示蚌斩,1415 方塊位置反了铆惑,無(wú)論如何也還原不聊了,這種情況是隨機(jī)出現(xiàn)的。到底是怎么回事呢员魏?經(jīng)過(guò)一番上網(wǎng)搜索丑蛤,確實(shí)如果只有兩個(gè)數(shù)字的位置反了,無(wú)論如何也還原不了的逆趋。那這是由什么造成的呢盏阶?還記得我們的二維數(shù)組是怎么產(chǎn)生的吧,隨機(jī)的闻书,也就是說(shuō)可能會(huì)隨機(jī)到無(wú)法還原的情況名斟。

如何避免這種情況呢?初始化數(shù)組時(shí)魄眉,所有的位置都是正確的數(shù)字砰盐,然后使用 move 進(jìn)行移動(dòng)打亂。

3.7 改進(jìn)完善

由于前面已經(jīng)將各個(gè)功能模塊單獨(dú)寫成了方法坑律,因此我們只需修改 onInit 方法即可岩梳。

# 初始化布局
def onInit(self):
    # 產(chǎn)生順序數(shù)組
    self.numbers = list(range(1, 16))
    self.numbers.append(0)

    # 將數(shù)字添加到二維數(shù)組
    for row in range(4):
        self.blocks.append([])
        for column in range(4):
            temp = self.numbers[row * 4 + column]

            if temp == 0:
                self.zero_row = row
                self.zero_column = column
            self.blocks[row].append(temp)

    # 打亂數(shù)組
    for i in range(500):
        random_num = random.randint(0, 3)
        self.move(Direction(random_num))

    self.updatePanel()

先生成一個(gè)順序數(shù)組,里面保存著[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0]晃择,然后轉(zhuǎn)為二維數(shù)組 blocks冀值,再后隨即移動(dòng)500次,最后添加到布局宫屠。

4列疗、完整代碼

import sys
import random
from enum import IntEnum
from PyQt5.QtWidgets import QLabel, QWidget, QApplication, QGridLayout, QMessageBox
from PyQt5.QtGui import QFont, QPalette
from PyQt5.QtCore import Qt

# 用枚舉類表示方向
class Direction(IntEnum):
    UP = 0
    DOWN = 1
    LEFT = 2
    RIGHT = 3


class NumberHuaRong(QWidget):
    """ 華容道主體 """
    def __init__(self):
        super().__init__()
        self.blocks = []
        self.zero_row = 0
        self.zero_column = 0
        self.gltMain = QGridLayout()

        self.initUI()

    def initUI(self):      
        # 設(shè)置方塊間隔
        self.gltMain.setSpacing(10)

        self.onInit()

        # 設(shè)置布局
        self.setLayout(self.gltMain)
        # 設(shè)置寬和高
        self.setFixedSize(400, 400)
        # 設(shè)置標(biāo)題
        self.setWindowTitle('數(shù)字華容道')
        # 設(shè)置背景顏色
        self.setStyleSheet("background-color:gray;")
        self.show()

    # 初始化布局
    def onInit(self):
        # 產(chǎn)生順序數(shù)組
        self.numbers = list(range(1, 16))
        self.numbers.append(0)

        # 將數(shù)字添加到二維數(shù)組
        for row in range(4):
            self.blocks.append([])
            for column in range(4):
                temp = self.numbers[row * 4 + column]

                if temp == 0:
                    self.zero_row = row
                    self.zero_column = column
                self.blocks[row].append(temp)

        # 打亂數(shù)組
        for i in range(500):
            random_num = random.randint(0, 3)
            self.move(Direction(random_num))

        self.updatePanel()

    # 檢測(cè)按鍵
    def keyPressEvent(self, event):
        key = event.key()
        if(key == Qt.Key_Up or key == Qt.Key_W):
            self.move(Direction.UP)
        if(key == Qt.Key_Down or key == Qt.Key_S):
            self.move(Direction.DOWN)
        if(key == Qt.Key_Left or key == Qt.Key_A):
            self.move(Direction.LEFT)
        if(key == Qt.Key_Right or key == Qt.Key_D):
            self.move(Direction.RIGHT)
        self.updatePanel()
        if self.checkResult():
            if QMessageBox.Ok == QMessageBox.information(self, '挑戰(zhàn)結(jié)果', '恭喜您完成挑戰(zhàn)!'):
                self.onInit()

    # 方塊移動(dòng)算法
    def move(self, direction):
        if(direction == Direction.UP): # 上
            if self.zero_row != 3:
                self.blocks[self.zero_row][self.zero_column] = self.blocks[self.zero_row + 1][self.zero_column]
                self.blocks[self.zero_row + 1][self.zero_column] = 0
                self.zero_row += 1
        if(direction == Direction.DOWN): # 下
            if self.zero_row != 0:
                self.blocks[self.zero_row][self.zero_column] = self.blocks[self.zero_row - 1][self.zero_column]
                self.blocks[self.zero_row - 1][self.zero_column] = 0
                self.zero_row -= 1
        if(direction == Direction.LEFT): # 左
            if self.zero_column != 3:
                self.blocks[self.zero_row][self.zero_column] = self.blocks[self.zero_row][self.zero_column + 1]
                self.blocks[self.zero_row][self.zero_column + 1] = 0
                self.zero_column += 1
        if(direction == Direction.RIGHT): # 右
            if self.zero_column != 0:
                self.blocks[self.zero_row][self.zero_column] = self.blocks[self.zero_row][self.zero_column - 1]
                self.blocks[self.zero_row][self.zero_column - 1] = 0
                self.zero_column -= 1

    def updatePanel(self):
        for row in range(4):
            for column in range(4):
                self.gltMain.addWidget(Block(self.blocks[row][column]), row, column)

        self.setLayout(self.gltMain)

    # 檢測(cè)是否完成
    def checkResult(self):
        # 先檢測(cè)最右下角是否為0
        if self.blocks[3][3] != 0:
            return False

        for row in range(4):
            for column in range(4):
                # 運(yùn)行到此處說(shuō)名最右下角已經(jīng)為0浪蹂,pass即可
                if row == 3 and column == 3:
                    pass
                #值是否對(duì)應(yīng)
                elif self.blocks[row][column] != row * 4 + column + 1:
                    return False

        return True

class Block(QLabel):
    """ 數(shù)字方塊 """

    def __init__(self, number):
        super().__init__()

        self.number = number
        self.setFixedSize(80, 80)

        # 設(shè)置字體
        font = QFont()
        font.setPointSize(30)
        font.setBold(True)
        self.setFont(font)

        # 設(shè)置字體顏色
        pa = QPalette()
        pa.setColor(QPalette.WindowText, Qt.white)
        self.setPalette(pa)

        # 設(shè)置文字位置
        self.setAlignment(Qt.AlignCenter)

        # 設(shè)置背景顏色\圓角和文本內(nèi)容
        if self.number == 0:
            self.setStyleSheet("background-color:white;border-radius:10px;")
        else:
            self.setStyleSheet("background-color:red;border-radius:10px;")
            self.setText(str(self.number))


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = NumberHuaRong()
    sys.exit(app.exec_())

5抵栈、總結(jié)

在做的過(guò)程中遇到最大的坑就是隨機(jī)數(shù)組導(dǎo)致無(wú)法還原。另外在做這個(gè)游戲的時(shí)候我已經(jīng)找到還原規(guī)律了坤次,這樣在測(cè)試的時(shí)候可以做完完整測(cè)試古劲,否則根本無(wú)法測(cè)試都挑戰(zhàn)成功那一步。

另外要對(duì)《最強(qiáng)大腦》做一下吐槽:這個(gè)項(xiàng)目根本就是有偏袒的缰猴,玩過(guò)的人會(huì)很快产艾,沒(méi)有玩過(guò)的找規(guī)律的時(shí)間就很長(zhǎng)。我在手機(jī)上玩4X4的最快還原用了 33 秒滑绒,對(duì)于該節(jié)目的冠軍(即便是玩過(guò))很是敬仰闷堡。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蹬挤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌棘幸,老刑警劉巖焰扳,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡吨悍,警方通過(guò)查閱死者的電腦和手機(jī)扫茅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)育瓜,“玉大人葫隙,你說(shuō)我怎么就攤上這事□锍穑” “怎么了恋脚?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)糟描。 經(jīng)常有香客問(wèn)我书妻,道長(zhǎng),這世上最難降的妖魔是什么见间? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任工猜,我火速辦了婚禮,結(jié)果婚禮上荒辕,老公的妹妹穿的比我還像新娘犹褒。我一直安慰自己,他們只是感情好李皇,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布宙枷。 她就那樣靜靜地躺著,像睡著了一般卓囚。 火紅的嫁衣襯著肌膚如雪诅病。 梳的紋絲不亂的頭發(fā)上粥烁,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天讨阻,我揣著相機(jī)與錄音,去河邊找鬼钝吮。 笑死板辽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的链患。 我是一名探鬼主播瓶您,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼贸毕!你這毒婦竟也來(lái)了夜赵?” 一聲冷哼從身側(cè)響起明棍,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤摊腋,失蹤者是張志新(化名)和其女友劉穎兴蒸,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體橙凳,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡笑撞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坚踩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓤狐。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡卧晓,死狀恐怖赴捞,靈堂內(nèi)的尸體忽然破棺而出郁稍,到底是詐尸還是另有隱情,我是刑警寧澤恢着,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布财破,位于F島的核電站,受9級(jí)特大地震影響左痢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜略步,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一定页、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧典徊,春花似錦、人聲如沸卒落。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)贾费。三九已至,卻和暖如春褂萧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背导犹。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谎痢,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓票从,卻偏偏與公主長(zhǎng)得像滨嘱,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子太雨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容