cocos2d-x3.14中国象棋AI(六)移动棋子2

有做完上一篇的同学会发现,移动棋子存在很多bug,这也是正常,我们只是写了移动的规则,具体的细节并没有完善,在这一篇我们就来把移动棋子完善一下。

Bug1:棋子移动后,选中框并不会消失,并且不能再选中其他棋子

解决方法:在棋子移动函数moveStone将LayerGameMain的_selectedId初始化为-1。

Bug2:解决选中同色棋子时直接移动。

解决方法:写一个颜色判断函数isSameColor在选中目标触点调用moveStone时,判断触点上是否有棋子并且判断棋子的颜色与选中棋子的颜色是否相同,如果存在棋子并且颜色相同,则将选中框移动到触点上的棋子。

Bug3:红棋移动到黑棋相同位置时,黑棋不会被”吃掉”

解决方法:在调用移动棋子函数moveStone时判断目标触点上的棋子是否为不同颜色,是的话将其设置为不可见。

Bug4:黑棋吃掉红棋后,黑棋无法再被选中

解决方法:这里存在精灵覆盖问题,但用Zorder又不好解决。这里给每个棋子添加一个标志变量_isdead,当棋子被吃了标志变量为true否则为false。然后我们在选中棋子时就可以在棋子遍历时通过标志变量去判断棋子是否相应触点的点击。

Bug5:没有轮流执棋

解决方法:在LayerGameMain上加一个_redTurn标志变量,当为true时红棋可以被选中,当为false时,黑棋可以被选中。每走完一步棋后执棋标志反转。
这里贴出Stone和LayerGameMain的整体代码:
Stone.h

#ifndef __STONE_H__
#define __STONE_H__

#include "cocos2d.h"
USING_NS_CC;

/*
布局棋子时因为棋盘左侧及下侧都空出一段,
所以在布局时,每个棋子都需要加上左侧空白段_offx及下侧空白段_offy。
*/

class Stone : public Sprite
{
public:
    static int _d;//棋子直径
    static int _offx;//棋子左侧空白段
    static int _offy;//棋子下侧空白段

    int _id;//棋子id
    int _col;//棋子的列号
    int _row;//棋子的行号
    bool _red;//标记当前棋子颜色
    bool _isdead;//判断该棋子是否被吃
    Texture2D *texture;

    //枚举出所有棋子类型
    enum TYPE{
        CHE,MA,XIANG,SHI,JIANG,PAO,BING
    };
    TYPE _type;

    static Stone* create(int id);
    virtual bool init(int id);
    void initStone(int id);//该函数用于初始化棋子成员变量
    void setStoneTexture(const char *filename);
    
    Point getPositionFromPlate();//该函数用于获取棋子相对于棋盘的位置
};

#endif

Stone.cpp

#include "Stone.h"

int Stone::_d = 32;
int Stone::_offx = 32;
int Stone::_offy = 16;

bool Stone::init(int id)
{
    if (!Sprite::init())
    {
        return false;
    }
    //设置纹理图片,此处与2.x版本有点区别,但是区别不大

    this->_isdead = false;
    initStone(id);
    setPosition(getPositionFromPlate());

    return true;
}

Stone* Stone::create(int id)
{
    Stone *ret = new Stone();
    if (ret && ret->init(id))
    {
        ret->autorelease();
    }
    else
    {
        delete ret;
        ret = nullptr;
    }
    return ret;
}

void Stone::initStone(int id)
{
    struct
    {
        TYPE type;
        int row;
        int col;
    }proper[9] = {
        {CHE,0,0},
        {MA,0,1},
        {XIANG,0,2},
        {SHI,0,3},
        {BING,3,2},
        {BING,3,0},
        {PAO,2,1},

        {JIANG,0,4},
        {BING,3,4}
    };
    _red = id < 16;
    if (id <= 8)
    {
        this->_id = id;
        this->_type = proper[id].type;
        this->_col = proper[id].col;
        this->_row = proper[id].row;
    }
    else if (id > 8 && id < 16)
    {
        this->_id = id;
        this->_type = proper[id - 9].type;
        this->_col = 8 - proper[id - 9].col;
        this->_row = proper[id - 9].row;
    }
    else if (id >= 16 && id <= 24)
    {
        this->_id = id;
        this->_type = proper[id - 16].type;
        this->_col = 8 - proper[id - 16].col;
        this->_row = 9 - proper[id - 16].row;
    }
    else if (id > 24)
    {
        this->_id = id;
        this->_type = proper[id - 25].type;
        this->_col = proper[id - 25].col;
        this->_row = 9 - proper[id - 25].row;
    }
    //按棋子类型选择棋子纹理
    switch (this->_type)
    {
        case CHE:
            if (_red)
                this->setStoneTexture("Stone/rche.png");
            else
                this->setStoneTexture("Stone/bche.png");
            break;
        case MA:
            if (_red)
                this->setStoneTexture("Stone/rma.png");
            else
                this->setStoneTexture("Stone/bma.png");
            break;
        case XIANG:
            if (_red)
                this->setStoneTexture("Stone/rxiang.png");
            else
                this->setStoneTexture("Stone/bxiang.png");
            break;
        case SHI:
            if (_red)
                this->setStoneTexture("Stone/rshi.png");
            else
                this->setStoneTexture("Stone/bshi.png");
            break;
        case PAO:
            if (_red)
                this->setStoneTexture("Stone/rpao.png");
            else
                this->setStoneTexture("Stone/bpao.png");
            break;
        case BING:
            if (_red)
                this->setStoneTexture("Stone/rbing.png");
            else
                this->setStoneTexture("Stone/bzu.png");
            break;
        case JIANG:
            if (_red)
                this->setStoneTexture("Stone/rshuai.png");
            else
                this->setStoneTexture("Stone/bjiang.png");
            break;
        default:
            break;
    }
}

Point Stone::getPositionFromPlate()
{
    Point ret = Point(Stone::_offx + this->_col*_d, Stone::_offy + this->_row*_d);

    return ret;
}

void Stone::setStoneTexture(const char* filename)
{
    texture = Director::getInstance()->getTextureCache()->addImage(filename);
    this->setTexture(texture);
    this->setTextureRect(Rect(0, 0, texture->getContentSize().width, texture->getContentSize().height));
}

LayerGameMain.h

#ifndef __LAYERGAMEMAIN_H__
#define __LAYERGAMEMAIN_H__

#include "cocos2d.h"
#include "Plate.h"
#include "Stone.h"

/*
cocos2d-x3.14.1版本将大部分类的前缀CC去掉了,不过有部分还是兼容,如果用旧的命名创建对应的类会有警告提示/错误提示
*/

USING_NS_CC;

class LayerGameMain : public Layer
{
public:
    static Scene* createScene();
    virtual bool init();
    CREATE_FUNC(LayerGameMain);

    //添加棋盘和棋子
    void addPlate();//用于在LayerGameMain上添加棋盘层/精灵
    void addStones();//用于在LayerGameMain上添加棋子层/精灵

    //触摸事件回调函数
    bool touchBeganCallBack(Touch *pTouch, Event *pEvent);
    void touchEndedCallBack(Touch *pTouch, Event *pEvent);

    //选中棋子相关
    Sprite *selectBox;//选中款精灵
    static int _selectedId;//标记当前选中的棋子id,没有点中为-1
    bool Screen2Plate(Point &point, int &row, int &col);//循环棋盘上所有点,通过Plate2Screen获取到棋子中心点,用引用输出中心点
    Point Plate2Screen(int row, int col);//获取棋子中心点返回到世界坐标给Screen2Plate
    void selectStone(Touch *pTouch);//选中棋子,将选中框精灵移动到选中的棋子上

    //获取棋子相关
    __Array *arrStone;//用于存放棋子
    Stone* getStoneFromArrById(int id);//通过棋子id从arrStone获取对应棋子
    int getStoneIdFromRowCol(int row, int col);//通过行列获取棋子id

    //棋子移动相关
    static bool _redTurn;
    void moveStone(Touch *pTouch);
    int getStoneCount(int row1, int col1, int row2, int col2);
    bool canMove(int moveid, int row, int col, int killid);//筛选移动规则
        //moveid为当前想要移动棋子的id,killid移动的目的点(-1为没有棋子),row和col为目的点的行列
        //炮、兵、将、车移动规则都有点相似
    bool canMoveChe(int moveid, int row, int col);//车移动规则
    bool canMovePao(int moveid, int row, int col, int killid);//炮移动规则
    bool canMoveBing(int moveid, int row, int col);//兵移动规则
    bool canMoveJiang(int moveid, int row, int col, int killid);//将移动规则
    bool canMoveMa(int moveid, int row, int col);//马移动规则
    bool canMoveXiang(int moveid, int row, int col);//象移动规则
    bool canMoveShi(int moveid, int row, int col);//士移动规则
    bool isSameColor(int clickedId);
};

#endif // !__LAYERGAMEMAIN_H__

LayerGameMain.cpp

#include "LayerGameMain.h"

int LayerGameMain::_selectedId = -1;
bool LayerGameMain::_redTurn = true;

Scene* LayerGameMain::createScene()
{
    auto ret = Scene::create();
    auto layer = LayerGameMain::create();
    ret->addChild(layer);

    return ret;
}

bool LayerGameMain::init()
{
    if (!Layer::init())
    {
        return false;
    }
    arrStone = __Array::create();
    arrStone->retain();//计数器+1,否则会被回收
    selectBox = Sprite::create("selected.png");
    selectBox->setVisible(false);
    selectBox->setZOrder(1000);//设置显示优先级
    this->addChild(selectBox);

    this->addPlate();
    this->addStones();

    //cocos2d-x3.14.1触摸事件,创建监听
    EventDispatcher* eventDispatcher = Director::getInstance()->getEventDispatcher();
    auto listener = EventListenerTouchOneByOne::create();
    listener->setEnabled(true);
    listener->setSwallowTouches(true);
    listener->onTouchBegan = CC_CALLBACK_2(LayerGameMain::touchBeganCallBack, this);
    listener->onTouchEnded = CC_CALLBACK_2(LayerGameMain::touchEndedCallBack, this);
    eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);

    return true;
}

void LayerGameMain::addPlate()
{
    Plate *plate = Plate::create();
    
    this->addChild(plate);
}

void LayerGameMain::addStones()
{
    int i = 0;
    Stone *stone;
    for (i = 0; i < 32; i++)
    {
        stone = Stone::create(i);
        arrStone->addObject(stone);//将棋子添加进数组
        this->addChild(stone);
    }
}

Stone* LayerGameMain::getStoneFromArrById(int stoneId)
{
    Stone *ret;
    ret = nullptr;
    if (stoneId > 0)
    {
        ret = (Stone *)arrStone->getObjectAtIndex(stoneId);
    }
    
    return ret;
}

bool LayerGameMain::touchBeganCallBack(Touch *pTouch, Event *pEvent)
{

    return true;
}
void LayerGameMain::touchEndedCallBack(Touch *pTouch, Event *pEvent)
{
    if (_selectedId == -1)
    {
        selectStone(pTouch);
    }
    else
    {
        moveStone(pTouch);
    }
}

bool LayerGameMain::Screen2Plate(Point &point, int &row, int &col)
{
    int distance = Stone::_d/2 * Stone::_d/2;//以半径作为距离
    for (row = 0; row <= 9; row++)
    {
        for (col = 0; col <= 8; col++)
        {
            Point ptCenter = Plate2Screen(row, col);
            if (distance > ptCenter.getDistanceSq(point))//获取点距的平方并比较,如果小于半径平方则为能选中中心点上的棋子
            {
                return true;
            }
        }
    }

    return false;
}
Point LayerGameMain::Plate2Screen(int row, int col)//该函数与Stone的getPositionFromPlate类似
{
    Point ret = Point(Stone::_offx + col*Stone::_d, Stone::_offy + row*Stone::_d);
    
    return ret;
}
int LayerGameMain::getStoneIdFromRowCol(int row, int col)
{
    int ret = -1;
    Ref *obj;
    CCARRAY_FOREACH(arrStone,obj)
    {
        Stone *stone = (Stone *)obj;
        if (stone->_row == row && stone->_col == col && !stone->_isdead)//已死去的棋子不能选中
        {
            ret = stone->_id;
            return ret;
        }
    }
    return ret;
}

void LayerGameMain::selectStone(Touch *pTouch)
{
    Point point = pTouch->getLocation();
    int row, col;
    if (!Screen2Plate(point, row, col))//点中范围不在棋盘上直接返回
    {
        selectBox->setVisible(false);
        return;
    }
    int clickedId = getStoneIdFromRowCol(row, col);
    if (clickedId < 0)//没选中棋子直接返回
    {
        selectBox->setVisible(false);
        return;
    }
    Stone *clickedStone = getStoneFromArrById(clickedId);
    if (clickedStone->_red != _redTurn)//如果没有轮到,不能选中、移动
    {
        return;
    }
    selectBox->setPosition(Plate2Screen(row, col));
    selectBox->setVisible(true);

    _selectedId = clickedId;//记录下棋子id
}

void LayerGameMain::moveStone(Touch *pTouch)
{
    Point ptClicked = pTouch->getLocation();
    int row, col;
    if (!Screen2Plate(ptClicked, row, col))//获取触点的行列,如果触点不在棋盘上直接返回
    {
        return;
    }
    int clickedId = getStoneIdFromRowCol(row, col);//获取触点上的棋子id,没有棋子为-1
    //如果点中相同颜色的棋子,选中框转移到最后选中的棋子上
    if (clickedId != -1 && isSameColor(clickedId))
    {
        Stone *clickedStone = getStoneFromArrById(clickedId);
        selectBox->setPosition(Plate2Screen(clickedStone->_row, clickedStone->_col));
        _selectedId = clickedId;
        return;
    }

    if (!canMove(_selectedId, row, col, clickedId))
    {
        return;
    }

    Point pt = Plate2Screen(row, col);
    MoveTo *to = MoveTo::create(0.3, pt);
    Stone *selectedStone = getStoneFromArrById(_selectedId);
    selectedStone->runAction(to);
    selectedStone->_row = row;
    selectedStone->_col = col;

    if (clickedId != -1)
    {
        Stone *clickedStone = getStoneFromArrById(clickedId);
        clickedStone->setVisible(false);
        clickedStone->_isdead = true;
    }

    //移动完毕初始化选中框和选中id
    selectBox->setVisible(false);
    _selectedId = -1;//移动完成后初始化为-1
    _redTurn = !_redTurn;
}

int LayerGameMain::getStoneCount(int row1, int col1, int row2, int col2)
{
    int ret = 0;//记录棋子的个数
    if (row1 != row2 && col1 != col2) return -1;//行列都不同,没有直线
    if (row1 == row2 && col1 == col2) return -1;//行列都相同,两点相同,不需要移动

    if (row1 == row2)//列相同
    {
        int mincol = col1 < col2 ? col1 : col2;//获取两点间较小的行
        int maxcol = col1 > col2 ? col1 : col2;//获取两点间较大的行
        for (int col = mincol+1; col < maxcol; col++)//遍历两点间所有点
        {
            if (getStoneIdFromRowCol(row1, col) > 0)//如果存在棋子,ret棋子计数器+1
            {
                ++ret;
            }
        }
    }
    else if(col1 == col2)
    {
        int minrow = row1 < row2 ? row1 : row2;//获取两点间较小的列
        int maxrow = row1 > row2 ? row1 : row2;//获取两点间较大的列
        for (int row = minrow + 1; row < maxrow; row++)//遍历两点间所有点
        {
            if (getStoneIdFromRowCol(row, col1) > 0)//如果存在棋子,ret棋子计数器+1
            {
                ++ret;
            }
        }
    }

    return ret;
}

bool LayerGameMain::canMove(int moveid, int row, int col, int killid)
{
    Stone *stone = getStoneFromArrById(moveid);
    switch (stone->_type)
    {
        case Stone::CHE:
            return canMoveChe(moveid, row, col);
            break;
        case Stone::JIANG:
            return canMoveJiang(moveid, row, col, killid);
            break;
        case Stone::BING:
            return canMoveBing(moveid, row, col);
            break;
        case Stone::PAO:
            return canMovePao(moveid, row, col, killid);
            break;
        case Stone::MA:
            return canMoveMa(moveid, row, col);
            break;
        case Stone::XIANG:
            return canMoveXiang(moveid, row, col);
            break;
        case Stone::SHI:
            return canMoveShi(moveid, row, col);
            break;
        default:
            break;
    }
    return false;
}
bool LayerGameMain::canMoveChe(int moveid, int row, int col)
{
    //车只有在两点间不存在棋子时才能走
    Stone *che = getStoneFromArrById(moveid);
    return getStoneCount(che->_row, che->_col, row, col) == 0;
}
bool LayerGameMain::canMovePao(int moveid, int row, int col, int killid)
{
    //炮可以行走只有两种情况:1.两点间没有棋子。2.两点间存在一个棋子,并且目的点也存在棋子。
    Stone *pao = getStoneFromArrById(moveid);
    if (killid == -1)
    {
        return getStoneCount(pao->_row, pao->_col, row, col) == 0;
    }

    return getStoneCount(pao->_row, pao->_col, row, col) == 1;
}
bool LayerGameMain::canMoveBing(int moveid, int row, int col)
{
    //兵行走规则:1.只能走一格。2.不能回退。3.过河前只能直走
    Stone *bing = getStoneFromArrById(moveid);
    int dRow = abs(bing->_row - row);
    int dCol = abs(bing->_col - col);
    int d = dRow * 10 + dCol;//此处类似标记效果,10等价于(1,0),1代表可移动,第一位说明可以左右移动一格,第二位说明可以上下移动一格
    if (d != 1 && d != 10)
    {
        return false;
    }

    if (bing->_red)
    {
        //兵不能回退
        if (row < bing->_row)
        {
            return false;
        }
        //兵过河前不能左右移动
        if (bing->_row <= 4 && bing->_row == row)
        {
            return false;
        }
    }
    else
    {
        if (row > bing->_row)
        {
            return false;
        }
        if (bing->_row >= 5 && bing->_row == row)
        {
            return false;
        }
    }

    return true;
}
bool LayerGameMain::canMoveJiang(int moveid, int row, int col, int killid)
{
    //将规则:1.一次只能移动一格。2.不能出九宫格。3.将帅照面时可以直接对杀。
    Stone *jiang = getStoneFromArrById(moveid);
    //将照面杀
    if (killid != -1)
    {
        Stone *kill = getStoneFromArrById(killid);
        if (kill->_type == Stone::JIANG)
        {
            return canMoveChe(moveid, row, col);//复用车的移动方法,减少代码量
        }
    }
    int dRow = abs(jiang->_row - row);
    int dCol = abs(jiang->_col - col);
    int d = dRow * 10 + dCol;//此处类似标记效果,10等价于(1,0),1代表可移动,第一位说明可以左右移动一格,第二位说明可以上下移动一格
    if (d != 1 && d != 10)
    {
        return false;
    }
    //将不能左右出九宫
    if (col < 3 || col > 5)
    {
        return false;
    }
    //将不能上下出九宫
    if (jiang->_red)
    {
        if (row > 2 || row <0)
        {
            return false;
        }
    }
    else
    {
        if (row < 7 || row >9)
        {
            return false;
        }
    }
    return true;
}
bool LayerGameMain::canMoveMa(int moveid, int row, int col)
{
    //马规则:1.马走日。2.卡住马脚不能移动。
    Stone *ma = getStoneFromArrById(moveid);
    int dRow = abs(ma->_row - row);
    int dCol = abs(ma->_col - col);
    int d = dRow * 10 + dCol;
    //12为横日,21为竖日,类似于(1,2)(2,1)做法
    if (d == 12 || d == 21)
    {
        int cRow, cCol;
        if (d == 12)
        {
            //蹩脚点
            cCol = (col + ma->_col) / 2;
            cRow = ma->_row;
        }
        else
        {
            cCol = ma->_col;
            cRow = (ma->_row + row) / 2;
        }
        //获取蹩脚点后判断蹩脚点是否有棋子
        if (getStoneIdFromRowCol(cRow, cCol) == -1)
        {
            return true;
        }
    }
    return false;
}
bool LayerGameMain::canMoveXiang(int moveid, int row, int col)
{
    //象规则:1.象走田。2.被卡象眼不能移动。3.象不能过河
    Stone *xiang = getStoneFromArrById(moveid);
    int dRow = abs(xiang->_row - row);
    int dCol = abs(xiang->_col - col);
    int d = dRow * 10 + dCol;
    if (d != 22)
    {
        return false;
    }

    int cRow, cCol;
    cRow = (row + xiang->_row) / 2;
    cCol = (col + xiang->_col) / 2;
    if (getStoneIdFromRowCol(cRow, cCol) != -1)//卡主香烟不能移动
    {
        return false;
    }

    if (xiang->_red)
    {
        if (row > 4) return false;
    }
    else
    {
        if (row < 5) return false;
    }

    return true;
}
bool LayerGameMain::canMoveShi(int moveid, int row, int col)
{
    //士规则:1.一次斜着走一格。2.不能出九宫格
    Stone *shi = getStoneFromArrById(moveid);
    int dRow = abs(shi->_row - row);
    int dCol = abs(shi->_col - col);
    int d = dRow * 10 + dCol;
    //只能斜线走
    if (d != 11)
    {
        return false;
    }
    //不能两侧走出九宫
    if (col<3 || col>5)
    {
        return false;
    }
    //不能上下走出九宫
    if (shi->_red)
    {
        if (row>2 || row<0)
        {
            return false;
        }
    }
    else
    {
        if (row<7 || row>9)
        {
            return false;
        }
    }

    return true;
}

bool LayerGameMain::isSameColor(int clickedId)
{
    Stone *clickedStone = getStoneFromArrById(clickedId);
    Stone *selectedStone = getStoneFromArrById(_selectedId);

    return selectedStone->_red == clickedStone->_red;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容