您的位置:澳门新葡8455最新网站 > Web前端 > 不错的俄罗斯方块程序代码,H5游戏开发

不错的俄罗斯方块程序代码,H5游戏开发

发布时间:2019-10-07 08:16编辑:Web前端浏览(198)

    H5游戏开辟:一笔画

    2017/11/07 · HTML5 · 游戏

    最先的小讲出处: 坑坑洼洼实验室   

    图片 1

    #include <windows.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <conio.h>

    H5游戏开拓:一笔画

    by leeenx on 2017-11-02

    单笔画是图论[科普](https://zh.wikipedia.org/wiki/%E5%9B%BE%E8%AE%BA)中二个响当当的标题,它起点于柯金沙萨堡七桥主题材料[科普](https://zh.wikipedia.org/wiki/%E6%9F%AF%E5%B0%BC%E6%96%AF%E5%A0%A1%E4%B8%83%E6%A1%A5%E9%97%AE%E9%A2%98)。化学家欧拉在她1736年登载的杂谈《柯马拉加堡的七桥》中不独有搞定了七桥难题,也提议了一笔画定理,顺带消除了一笔画难题。用图论的术语来讲,对于三个加以的连通图[科普](https://zh.wikipedia.org/wiki/%E8%BF%9E%E9%80%9A%E5%9B%BE)留存一条恰好含有全体线段况且未有再次的路子,这条路线就是「一笔画」。

    搜索连通图那条路线的进度正是「一笔画」的娱乐进度,如下:

    图片 2

    class Console
    {
    public:
    Console()
    {
    hStdOutput = INVALID_HANDLE_VALUE;
    hStdError = INVALID_HANDLE_VALUE;
    }
    bool Open( void )
    {
    hStdOutput = GetStdHandle( STD_OUTPUT_HANDLE );
    hStdError = GetStdHandle( STD_ERROR_HANDLE );
    return INVALID_HANDLE_VALUE!=hStdOutput && INVALID_HANDLE_VALUE!=hStdError;
    }
    inline bool SetTitle( char* title ) // 设置标题
    {
    return TRUE==SetConsoleTitle(title);
    }
    bool RemoveCursor( void ) // 去处光标
    {
    CONSOLE_CURSOR_INFO cci;
    if( !GetConsoleCursorInfo( hStdOutput, &cci ) ) return false;
    cci.bVisible = false;
    if( !SetConsoleCursorInfo( hStdOutput, &cci ) ) return false;
    if( !GetConsoleCursorInfo( hStdError, &cci ) ) return false;
    cci.bVisible = false;
    if( !SetConsoleCursorInfo( hStdError, &cci ) ) return false;
    return true;
    }
    bool SetWindowRect( short x, short y ) // 设置窗体尺寸
    {
    SMALL_RECT wrt = { 0, 0, x, y };
    if( !SetConsoleWindowInfo( hStdOutput, TRUE, &wrt ) ) return false;
    if( !SetConsoleWindowInfo( hStdError, TRUE, &wrt ) ) return false;
    return true;
    }
    bool SetBufSize( short x, short y ) // 设置缓冲尺寸
    {
    COORD coord = { x, y };
    if( !SetConsoleScreenBufferSize( hStdOutput, coord ) ) return false;
    if( !SetConsoleScreenBufferSize( hStdError, coord ) ) return false;
    return true;
    }

    游玩的兑现

    「一笔画」的落到实处不复杂,小编把贯彻进程分成两步:

    1. 底图绘制
    2. 交互绘制

    「底图绘制」把连通图以「点线」的款型显得在画布上,是游戏最轻松实现的一部分;「交互绘制」是客户绘制解题路线的进程,这几个进度会重视是处理点与点动态成线的逻辑。

    bool GotoXY( short x, short y ) // 移动光标
    {
    COORD coord = { x, y };
    if( !SetConsoleCursorPosition( hStdOutput, coord ) ) return false;
    if( !SetConsoleCursorPosition( hStdError, coord ) ) return false;
    return true;
    }
    bool SetColor( WORubiconD color ) // 设置前景观/背景象
    {
    if( !SetConsoleTextAttribute( hStdOutput, color ) ) return false;
    if( !SetConsoleTextAttribute( hStdError, color ) ) return false;
    return true;
    }
    bool OutputString( const char* pstr, size_t len=0 ) // 输出字符串
    {
    DWORD n = 0;
    return TRUE==WriteConsole( hStdOutput, pstr, len?len:strlen(pstr), &n, NULL );
    }

    底图绘制

    「一笔画」是多关卡的二十三17日游形式,小编决定把关卡(连通图)的定制以多少个布署接口的款式对外暴光。对外揭露关卡接口必要有一套描述连通图形状的行业内部,而在作者前面有三个挑选:

    • 点记法
    • 线记法

    举个连通图 —— 五角星为例来讲一下那四个选拔。

    图片 3

    点记法如下:

    JavaScript

    levels: [ // 当前关卡 { name: "五角星", coords: [ {x: Ax, y: Ay}, {x: Bx, y: By}, {x: Cx, y: Cy}, {x: Dx, y: Dy}, {x: Ex, y: Ey}, {x: Ax, y: Ay} ] } ... ]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    levels: [
    // 当前关卡
    {
    name: "五角星",
    coords: [
    {x: Ax, y: Ay},
    {x: Bx, y: By},
    {x: Cx, y: Cy},
    {x: Dx, y: Dy},
    {x: Ex, y: Ey},
    {x: Ax, y: Ay}
    ]
    }
    ...
    ]

    线记法如下:

    JavaScript

    levels: [ // 当前关卡 { name: "五角星", lines: [ {x1: Ax, y1: Ay, x2: Bx, y2: By}, {x1: Bx, y1: By, x2: Cx, y2: Cy}, {x1: Cx, y1: Cy, x2: Dx, y2: Dy}, {x1: Dx, y1: Dy, x2: Ex, y2: Ey}, {x1: Ex, y1: Ey, x2: Ax, y2: Ay} ] } ]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    levels: [
    // 当前关卡
    {
    name: "五角星",
    lines: [
    {x1: Ax, y1: Ay, x2: Bx, y2: By},
    {x1: Bx, y1: By, x2: Cx, y2: Cy},
    {x1: Cx, y1: Cy, x2: Dx, y2: Dy},
    {x1: Dx, y1: Dy, x2: Ex, y2: Ey},
    {x1: Ex, y1: Ey, x2: Ax, y2: Ay}
    ]
    }
    ]

    「点记法」记录关卡通关的一个答案,即端点要按自然的相继寄放到数组 coords中,它是有序性的记录。「线记法」通过两点描述连通图的线条,它是冬日的笔录。「点记法」最大的优势是表现更简短,但它必得记录两个通关答案,作者只是关卡的搬运工不是关卡创建者,所以作者最后摘取了「线记法」。:)

    bool OutputStringNoMove( short x, short y, const char* pstr, size_t len=0 ) // 输出字符串
    {
    COORD coord = { x, y };
    DWORD n = 0;
    return TRUE==WriteConsoleOutputCharacter( hStdOutput, pstr, len?len:strlen(pstr), coord, &n );
    }
    private:
    HANDLE hStdOutput;
    HANDLE hStdError;
    };

    互相绘制

    在画布上绘制路线,从视觉上正是「选用或一而再连通图端点」的历程,这么些历程必要缓和2个难题:

    • 手指下是或不是有端点
    • 入选点到待选中式点心时期是还是不是成线

    搜罗连通图端点的坐标,再监听手指滑过的坐标能够知晓「手指下是或不是有一点点」。以下伪代码是搜罗端点坐标:

    JavaScript

    // 端点坐标音信 let coords = []; lines.forEach(({x1, y1, x2, y2}) => { // (x1, y1) 在 coords 数组不真实 if(!isExist(x1, y1)) coords.push([x1, y1]); // (x2, y2) 在 coords 数组不设有 if(!isExist(x2, y2)) coords.push([x2, y2]); });

    1
    2
    3
    4
    5
    6
    7
    8
    // 端点坐标信息
    let coords = [];
    lines.forEach(({x1, y1, x2, y2}) => {
    // (x1, y1) 在 coords 数组不存在
    if(!isExist(x1, y1)) coords.push([x1, y1]);
    // (x2, y2) 在 coords 数组不存在
    if(!isExist(x2, y2)) coords.push([x2, y2]);
    });

    以下伪代码是监听手指滑动:

    JavaScript

    easel.addEventListener("touchmove", e => { let x0 = e.targetTouches[0].pageX, y0 = e.targetTouches[0].pageY; // 端点半径 ------ 取连通图端点半径的2倍,进步活动端体验 let r = radius * 2; for(let [x, y] of coords){ if(Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0), 2) <= r){ // 手指下有端点,判别能还是不可能连线 if(canConnect(x, y)) { // todo } break; } } })

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    easel.addEventListener("touchmove", e => {
    let x0 = e.targetTouches[0].pageX, y0 = e.targetTouches[0].pageY;
    // 端点半径 ------ 取连通图端点半径的2倍,提升移动端体验
    let r = radius * 2;
    for(let [x, y] of coords){
    if(Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0), 2) <= r){
    // 手指下有端点,判断能否连线
    if(canConnect(x, y)) {
    // todo
    }
    break;
    }
    }
    })

    在未绘制任何线段或端点从前,手指滑过的肆意端点都会被看做「一笔画」的早先点;在绘制了线段(或有选中式茶食)后,手指滑过的端点能或不能够与选中式茶食串连成线段须求依附现成标准实行判别。

    图片 4

    上海体育场所,点A与点B可三回九转成线段,而点A与点C不可能三回九转。作者把「可以与钦命端点连接成线段的端点称作立竿见影连接点」。连通图端点的管事连接点从连通图的线条中提取:

    JavaScript

    coords.forEach(coord => { // 有效连接点(坐标)挂载在端点坐标下 coord.validCoords = []; lines.forEach(({x1, y1, x2, y2}) => { // 坐标是当前线段的源点 if(coord.x === x1 && coord.y === y1) { coord.validCoords.push([x2, y2]); } // 坐标是近来线段的顶峰 else if(coord.x === x2 && coord.y === y2) { coord.validCoords.push([x1, y1]); } }) })

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    coords.forEach(coord => {
    // 有效连接点(坐标)挂载在端点坐标下
    coord.validCoords = [];
    lines.forEach(({x1, y1, x2, y2}) => {
    // 坐标是当前线段的起点
    if(coord.x === x1 && coord.y === y1) {
    coord.validCoords.push([x2, y2]);
    }
    // 坐标是当前线段的终点
    else if(coord.x === x2 && coord.y === y2) {
    coord.validCoords.push([x1, y1]);
    }
    })
    })

    But…有效连接点只可以决断八个点是还是不是为底图的线条,那只是三个静态的参考,在事实上的「交互绘制」中,会境遇以下景况:

    图片 5
    如上海教室,AB已串连成线段,当前选中式茶食B的实惠连接点是 A 与 C。AB 已经接二连三成线,如若 BA 也串连成线段,那么线段就再也了,所以那时候 BA 不能够成线,唯有 AC 工夫成线。

    对选中式茶食来讲,它的有用连接点有三种:

    • 与选中式点心「成线的得力连接点」
    • 与选中式茶食「未成线的实用连接点」

    内部「未成线的灵光连接点」技巧参加「交互绘制」,并且它是动态的。

    图片 6

    回头本节内容开头提的多个难点「手指下是不是有端点」 与 「选中式茶食到待选中式茶食时期是不是成线」,其实可统一为二个主题材料:手指下是还是不是留存「未成线的有效连接点」。只须把监听手指滑动遍历的数组由连通图全数的端点坐标 coords 替换为如今选中式点心的「未成线的管用连接点」就能够。

    迄今停止「单笔画」的最首要意义已经落实。能够超过体验一下:

    图片 7

    const char bg[] =
    "┏━━━━━━━━━━━┓ "
    "┃■■■■■■■■■■■┃ ←↓→ ↑"
    "┃■■■■■■■■■■■┃ Begin "
    "┃■■■■■■■■■■■┃ Voice = Yes"
    "┃■■■■■■■■■■■┃ Sleep "
    "┃■■■■■■■■■■■┃ Quit "
    "┃■■■■■■■■■■■┃ "
    "┃■■■■■■■■■■■┃ "
    "┃■■■■■■■■■■■┃ NEXT "
    "┃■■■■■■■■■■■┃┏━━━━┓"
    "┃■■■■■■■■■■■┃┃    ┃"
    "┃■■■■■■■■■■■┃┃    ┃"
    "┃■■■■■■■■■■■┃┗━━━━┛"
    "┃■■■■■■■■■■■┃ LEVEL "
    "┃■■■■■■■■■■■┃┏━━━━┓"
    "┃■■■■■■■■■■■┃┃ 0┃"
    "┃■■■■■■■■■■■┃┗━━━━┛"
    "┃■■■■■■■■■■■┃ SCORE "
    "┃■■■■■■■■■■■┃┏━━━━┓"
    "┃■■■■■■■■■■■┃┃ 00000┃"
    "┗━━━━━━━━━━━┛┗━━━━┛";

    机动识图

    我在录入关卡配置时,开掘一个7条边以上的对接图很轻易录错或录重线段。我在图谋是还是不是开辟四个自动识别图形的插件,毕竟「一笔画」的图纸是有平整的几何图形。

    图片 8

    上边的卡子「底图」,一眼就足以识出三个颜色:

    • 白底
    • 端点颜色
    • 线条颜色

    再者那三种颜色在「底图」的面积大小顺序是:白底 > 线段颜色 > 端点颜色。底图的「搜集色值表算法」很简短,如下伪代码:

    JavaScript

    let imageData = ctx.getImageData(); let data = imageData.data; // 色值表 let clrs = new Map(); for(let i = 0, len = data.length; i < len; i += 4) { let [r, g, b, a] = [data[i], data[i + 1], data[i + 2], data[i + 3]]; let key = `rgba(${r}, ${g}, ${b}, ${a})`; let value = clrs.get(key) || {r, g, b, a, count: 0}; clrs.has(key) ? ++value.count : clrs.set(rgba, {r, g, b, a, count}); }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let imageData = ctx.getImageData();
    let data = imageData.data;
    // 色值表
    let clrs = new Map();
    for(let i = 0, len = data.length; i < len; i += 4) {
    let [r, g, b, a] = [data[i], data[i + 1], data[i + 2], data[i + 3]];
    let key = `rgba(${r}, ${g}, ${b}, ${a})`;
    let value = clrs.get(key) || {r, g, b, a, count: 0};
    clrs.has(key) ? ++value.count : clrs.set(rgba, {r, g, b, a, count});
    }

    对于连通图来说,只要把端点识别出来,连通图的概貌也就出去了。

    const char bk[7][4][4][4] =
    {
    {
    { { 0,1,1,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
    { { 1,0,0,0 },{ 1,1,0,0 },{ 0,1,0,0 },{ 0,0,0,0 } },
    { { 0,1,1,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
    { { 1,0,0,0 },{ 1,1,0,0 },{ 0,1,0,0 },{ 0,0,0,0 } }
    }
    ,
    {
    { { 1,1,0,0 },{ 0,1,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
    { { 0,1,0,0 },{ 1,1,0,0 },{ 1,0,0,0 },{ 0,0,0,0 } },
    { { 1,1,0,0 },{ 0,1,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
    { { 0,1,0,0 },{ 1,1,0,0 },{ 1,0,0,0 },{ 0,0,0,0 } }
    }
    ,
    {
    { { 1,1,1,0 },{ 1,0,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
    { { 1,0,0,0 },{ 1,0,0,0 },{ 1,1,0,0 },{ 0,0,0,0 } },
    { { 0,0,1,0 },{ 1,1,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
    { { 1,1,0,0 },{ 0,1,0,0 },{ 0,1,0,0 },{ 0,0,0,0 } }
    }
    ,
    {
    { { 1,1,1,0 },{ 0,0,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
    { { 1,1,0,0 },{ 1,0,0,0 },{ 1,0,0,0 },{ 0,0,0,0 } },
    { { 1,0,0,0 },{ 1,1,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
    { { 0,1,0,0 },{ 0,1,0,0 },{ 1,1,0,0 },{ 0,0,0,0 } }
    }
    ,
    {
    { { 1,1,0,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
    { { 1,1,0,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
    { { 1,1,0,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
    { { 1,1,0,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } }
    }
    ,
    {
    { { 0,1,0,0 },{ 1,1,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
    { { 0,1,0,0 },{ 1,1,0,0 },{ 0,1,0,0 },{ 0,0,0,0 } },
    { { 1,1,1,0 },{ 0,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
    { { 1,0,0,0 },{ 1,1,0,0 },{ 1,0,0,0 },{ 0,0,0,0 } }
    }
    ,
    {
    { { 1,1,1,1 },{ 0,0,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
    { { 1,0,0,0 },{ 1,0,0,0 },{ 1,0,0,0 },{ 1,0,0,0 } },
    { { 1,1,1,1 },{ 0,0,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
    { { 1,0,0,0 },{ 1,0,0,0 },{ 1,0,0,0 },{ 1,0,0,0 } }
    }
    };

    端点识别

    辩白上,通过收罗的「色值表」能够一直把端点的坐标志别出来。笔者设计的「端点识别算法」分以下2步:

    1. 按像素扫描底图直到遭遇「端点颜色」的像素,步入第二步
    2. 从底图上去掉端点并记下它的坐标,再次来到继续第一步

    伪代码如下:

    JavaScript

    for(let i = 0, len = data.length; i < len; i += 4) { let [r, g, b, a] = [data[i], data[i + 1], data[i + 2], data[i + 3]]; // 当前像素颜色属于端点 if(isBelongVertex(r, g, b, a)) { // 在 data 中清空端点 vertex = clearVertex(i); // 记录端点音信vertexes.push(vertext); } }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    for(let i = 0, len = data.length; i < len; i += 4) {
    let [r, g, b, a] = [data[i], data[i + 1], data[i + 2], data[i + 3]];
    // 当前像素颜色属于端点
    if(isBelongVertex(r, g, b, a)) {
    // 在 data 中清空端点
    vertex = clearVertex(i);
    // 记录端点信息
    vertexes.push(vertext);
    }
    }

    But… 下面的算法只好跑无损图。小编在应用了一张手提式有线电话机截屏做测量试验的时候开采,摘采到的「色值表」长度为 5000+ !那平昔导致端点和线条的色值无法直接得到。

    通过解析,能够窥见「色值表」里大部分色值都以左近的,也正是在原来的「搜集色值表算法」的根底上增多叁个相近颜色过滤即能够搜索端点和线条的主色。伪代码完毕如下:

    JavaScript

    let lineColor = vertexColor = {count: 0}; for(let clr of clrs) { // 与底色周边,跳过 if(isBelongBackground(clr)) continue; // 线段是数码第二多的颜色,端点是第三多的水彩 if(clr.count > lineColor.count) { [vertexColor, lineColor] = [lineColor, clr] } }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    let lineColor = vertexColor = {count: 0};
    for(let clr of clrs) {
    // 与底色相近,跳过
    if(isBelongBackground(clr)) continue;
    // 线段是数量第二多的颜色,端点是第三多的颜色
    if(clr.count > lineColor.count) {
    [vertexColor, lineColor] = [lineColor, clr]
    }
    }

    取到端点的主色后,再跑一回「端点识别算法」后居识别出 203 个端点!那是干吗吧?

    图片 9

    上海教室是推广5倍后的底图局地,铁红端点的四周和在那之中充斥着多量噪点(杂色块)。事实上在「端点识别」进程中,由于噪点的留存,把原来的端点被分解成贰11个或数十三个小端点了,以下是跑过「端点识别算法」后的底图:

    图片 10

    通过上海教室,可以直观地搜查缉获一个定论:识别出来的小端点只在目的(大)端点上集中分布,而且大端点范围内的小端点叠加交错。

    一旦把叠合交错的小端点归并成二个四头点,那么这几个大端点将蛮好像目的端点。小端点的合并伪代码如下:

    JavaScript

    for(let i = 0, len = vertexes.length; i < len - 1; ++i) { let vertexA = vertexes[i]; if(vertextA === undefined) continue; // 注意这里 j = 0 实际不是 j = i +1 for(let j = 0; j < len; ++j) { let vertexB = vertexes[j]; if(vertextB === undefined) continue; // 点A与点B有增大,点B合併到点A并删除点B if(isCross(vertexA, vertexB)) { vertexA = merge(vertexA, vertexB); delete vertexA; } } }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    for(let i = 0, len = vertexes.length; i < len - 1; ++i) {
    let vertexA = vertexes[i];
    if(vertextA === undefined) continue;
    // 注意这里 j = 0 而不是 j = i +1
    for(let j = 0; j < len; ++j) {
    let vertexB = vertexes[j];
    if(vertextB === undefined) continue;
    // 点A与点B有叠加,点B合并到点A并删除点B
    if(isCross(vertexA, vertexB)) {
    vertexA = merge(vertexA, vertexB);
    delete vertexA;
    }
    }
    }

    加了小端点归并算法后,「端点识别」的准确度就上来了。经我本地质度量试已经得以 百分之百 识别有损的连片图了。

    const WORD COLOR_A = FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_INTENSITY; // 运动中的颜色
    const WORD COLOR_B = FOREGROUND_GREEN; // 固定不动的颜料
    const WORD COLOR_C = FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE; // 空白处的水彩

    线条识别

    作者分五个步骤达成「线段识别」:

    1. 加以的三个端点连接成线,并募集连线上N个「样本点」;
    2. 遍历样本点像素,假使像素色值不对等线段色值则代表那多少个端点之间不设有线段

    怎么着搜集「样式点」是个难点,太密集会潜濡默化属性;太疏松精准度不可能担保。

    在小编前面有七个选项:N 是常量;N 是变量。
    假设 N === 5。局部提取「样式点」如下:

    图片 11

    上图,会识别出三条线条:AB, BC 和 AC。而实际,AC不可能成线,它只是因为 AB 和 BC 视觉上共一线的结果。当然把 N 值向上升高能够消除那一个标题,可是 N 作为常量的话,这些常量的取量须要靠经验来推断,果然扬弃。

    为了幸免 AB 与 BC 同处向来线时 AC 被辨认成线段,其实很简单 —— 多少个「样本点」的间隔小于或等于端点直径
    假设 N = S / (2 * R),S 表示两点的相距,翼虎表示端点半径。局地提取「样式点」如下:

    图片 12

    如上图,成功地绕过了 AC。「线段识别算法」的伪代码达成如下:

    JavaScript

    for(let i = 0, len = vertexes.length; i < len - 1; ++i) { let {x: x1, y: y1} = vertexes[i]; for(let j = i + 1; j < len; ++j) { let {x: x2, y: y2} = vertexes[j]; let S = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)); let N = S / (R * 2); let stepX = (x1 - x2) / N, stepY = (y1 - y2) / n; while(--N) { // 样本点不是线段色 if(!isBelongLine(x1 + N * stepX, y1 + N * stepY)) break; } // 样本点都合格 ---- 表示两点成线,保存 if(0 === N) lines.push({x1, y1, x2, y2}) } }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    for(let i = 0, len = vertexes.length; i < len - 1; ++i) {
    let {x: x1, y: y1} = vertexes[i];
    for(let j = i + 1; j < len; ++j) {
    let {x: x2, y: y2} = vertexes[j];
    let S = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
    let N = S / (R * 2);
    let stepX = (x1 - x2) / N, stepY = (y1 - y2) / n;
    while(--N) {
    // 样本点不是线段色
    if(!isBelongLine(x1 + N * stepX, y1 + N * stepY)) break;
    }
    // 样本点都合格 ---- 表示两点成线,保存
    if(0 === N) lines.push({x1, y1, x2, y2})
    }
    }

    bool voice = true;
    int score = 0, level = 0;
    char data[19][11] = { 0 };
    int next = -1;
    int x=4, y=-2, c=-1, z=0; // x坐标,坐标,当前方块,方向

    性情优化

    出于「自动识图」须要对图像的的像素点实行扫描,那么质量确实是个必要关切的标题。作者设计的「自动识图算法」,在辨明图像的进度中须求对图像的像素做五回扫描:「收集色值表」 与 「搜集端点」。在扫描次数上实际很难下跌了,但是对于一张 750 * 1334 的底图来讲,「自动识图算法」需求遍历三次长度为 750 * 1334 * 4 = 4,002,000 的数组,压力依然会有的。作者是从压缩被扫描数组的尺寸来提高质量的。

    被围观数组的尺码怎么收缩?
    作者直接通过压缩画布的尺码来达到裁减被围观数组尺寸的。伪代码如下:

    JavaScript

    // 要削减的翻番 let resolution = 4; let [width, height] = [img.width / resolution >> 0, img.height / resolution >> 0]; ctx.drawImage(img, 0, 0, width, height); let imageData = ctx.getImageData(), data = imageData;

    1
    2
    3
    4
    5
    // 要压缩的倍数
    let resolution = 4;
    let [width, height] = [img.width / resolution >> 0, img.height / resolution >> 0];
    ctx.drawImage(img, 0, 0, width, height);
    let imageData = ctx.getImageData(), data = imageData;

    把源图片降低4倍后,得到的图纸像素数组唯有原本的 4^2 = 16倍。这在性质上是相当的大的升高。

    Console csl; // 定义调节台对象

    动用「自动识图」的建议

    纵然作者在该地质度量试的时候能够把具有的「底图」识别出来,不过并不能保证别的开拓者上传的图样是不是被很好的辨识出来。笔者提议,能够把「自动识图」做为一个单身的工具使用。

    小编写了二个「自动识图」的独立工具页面:
    能够在这些页素不相识成对应的卡子配置。

    void VoiceBeep( void )
    {
    if( voice )
    Beep( 1760, 10 );
    }

    结语

    上面是本文介绍的「一笔画」的线上 DEMO 的二维码:

    图片 13

    游玩的源码托管在:
    其间玩耍达成的主心骨代码在:
    自行识图的代码在:

    感激耐心阅读完本作品的读者。本文仅代表小编的个人观点,如有不妥之处请多多支持。

    谢谢您的阅读,本文由 坑坑洼洼实验室 版权全体。假若转发,请表明出处:凹凸实验室()

    1 赞 1 收藏 评论

    图片 14

    void DrawScoreLevel( void ) // 绘制得分
    {
    char tmp[6];
    sprintf( tmp, "%05d", score );
    csl.OutputStringNoMove( 31, 19, tmp, 5 );
    sprintf( tmp, "%1d", level );
    csl.OutputStringNoMove( 35, 15, tmp, 1 );
    }

    void DrawVoice( void )
    {
    csl.OutputStringNoMove( 35, 3, voice?"Yes":"No " );
    }

    void DrawNext( void ) // 绘制 "next框" 中的图形
    {
    for( int i=0; i<2; ++i )
    {
    for( int j=0; j<4; ++j )
    {
    csl.OutputStringNoMove( 28+j*2, 10+i, bk[next][0][i][j]==0?" ":"■", 2 );
    }
    }
    }

    void DrawOver( void ) // 游戏甘休
    {
    csl.OutputStringNoMove( 28, 10, "GAME" );
    csl.OutputStringNoMove( 28, 11, "OVER" );
    }

    void Draw( WORD color )
    {
    for( int i=0; i<4; ++i )
    {
    if( y+i<0 || y+i>= 19 ) continue;
    for( int j=0; j<4; ++j )
    {
    if( bk[c][z][i][j] == 1 )
    {
    csl.SetColor( color );
    csl.GotoXY( 2+x*2+j*2, 1+y+i );
    csl.OutputString( "■", 2 );
    }
    }
    }
    }

    bool IsFit( int x, int y, int c, int z ) // 给定的x,y,c,z是或不是行得通
    {
    for( int i=0; i<4; ++i )
    {
    for( int j=0; j<4; ++j )
    {
    if( bk[c][z][i][j]==1 )
    {
    if( y+i < 0 ) continue;
    if( y+i>=19 || x+j<0 || x+j>=11 || data[y+i][x+j]==1 ) return false;
    }
    }
    }
    return true;
    }

    void RemoveRow( void ) // 消行
    {
    const char FULLLINE[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
    int linecount = 0;
    for( int i=0; i<19; ++i )
    {
    if( 0 == memcmp( data[i], FULLLINE, 11 ) )
    {
    ++linecount;
    for( int m=0; m<11; ++m )
    {
    for( int n=i; n>1; --n )
    {
    data[n][m] = data[n-1][m];

    csl.SetColor( data[n][m]==1?COLOR_B:COLOR_C );
    csl.GotoXY( 2+m*2, 1+n );
    csl.OutputString( "■", 2 );
    }
    data[0][m] = 0;
    csl.OutputStringNoMove( 2+m*2, 1, "■", 2 );
    }
    }
    }
    char data[19][11] = { 0 };

    if( linecount == 0 ) return;
    int _score = 0;
    switch( linecount )
    {
    case 1: _score = 100; break;
    case 2: _score = 300; break;
    case 3: _score = 700; break;
    case 4: _score = 1500;break;
    }
    score += _score;
    if( score > 99999 ) score = 99999;
    level = score/10000;
    DrawScoreLevel();
    }

    void MoveTrans( void ) // 逆时针翻转
    {
    if( IsFit( x, y, c, (z+1)%4 ) )
    {
    VoiceBeep();
    Draw( COLOR_C );
    z=(z+1)%4;
    Draw( COLOR_A );
    }
    }

    void MoveLeft( void ) // 向左移
    {
    if( IsFit( x-1, y, c, z ) )
    {
    VoiceBeep();
    Draw( COLOR_C );
    --x;
    Draw( COLOR_A );
    }
    }

    void MoveRight( void ) // 向右移
    {
    if( IsFit( x+1, y, c, z ) )
    {
    VoiceBeep();
    Draw( COLOR_C );
    ++x;
    Draw( COLOR_A );
    }
    }

    void MoveDown( void ) // 向下移
    {
    if( IsFit( x, y+1, c, z ) )
    {
    VoiceBeep();
    Draw( COLOR_C );
    ++y;
    Draw( COLOR_A );
    }
    else if( y != -2 ) // 触底
    {
    Draw( COLOR_B );

    for( int i=0; i<4; ++i )
    {
    if( y+i<0 ) continue;
    for( int j=0; j<4; ++j )
    {
    if( bk[c][z][i][j] == 1 )
    {
    data[y+i][x+j] = 1;
    }
    }
    }

    RemoveRow();

    x=4, y=-2, c=next, z=0;
    next = rand()%7;
    DrawNext();
    }
    else // 游戏甘休
    {
    DrawOver();
    }
    }

    void MessageDeal( void )
    {
    int cycle = 10 - level;
    for( ; ; )
    {
    for( int i=0; i<cycle; ++i )
    {
    if( _kbhit() )
    {
    switch( _getch() )
    {
    case 'Q':
    case 'q': // 退出
    return;
    break;
    case 'S': // 暂停
    case 's':
    for( ; ; )
    {
    switch( _getch() )
    {
    case 'Q':
    case 'q': // 退出
    return;
    case 'V': // 声音
    case 'v':
    voice = !voice;
    DrawVoice();
    break;
    case 'S':
    case 's':
    goto LABLE_CONTINUE;
    break;
    }
    }
    LABLE_CONTINUE:
    break;
    case 'V': // 声音
    case 'v':
    voice = !voice;
    DrawVoice();
    break;
    case 0xe0: // ←↓→ ↑
    switch( _getch() )
    {
    case 0x4B: // ←
    MoveLeft();
    break;
    case 0x50: // ↓
    MoveDown();
    break;
    case 0x4d: // →
    MoveRight();
    break; // ↑ 变形
    case 0x48:
    MoveTrans();
    default:
    break;
    }
    break;
    default:
    break;
    }
    }

    Sleep( 55 );
    }
    MoveDown();
    }
    }

    int main()
    {
    csl.Open();
    // 设置标题
    csl.SetTitle( "俄罗斯方块 阿尔法" );
    // 去处光标
    csl.RemoveCursor();
    // 设置窗体尺寸
    csl.SetWindowRect( 38-1, 21-1 );
    // 设置缓冲尺寸
    csl.SetBufSize( 38, 21 );
    // 输出背景字符
    csl.OutputStringNoMove( 0,0,bg );
    // 设置随机种子
    srand( time(0) );

    next = rand()%7;
    DrawNext();
    {
    for( char c = (char)_getch(); c!='B'&&c!='b'; c=(char)_getch() ) // 开始 Begin
    {
    if( c=='V' || c=='v' ) // 铃声 Vocie
    {
    if( voice )
    {
    voice = false;
    csl.OutputStringNoMove( 35, 3, "No " );
    }
    else
    {
    voice = true;
    csl.OutputStringNoMove( 35, 3, "Yes" );
    }
    }
    }
    }
    x=4, y=-2, c=next, z=0;
    next = rand()%7;
    DrawNext();

    MessageDeal();
    return 0;
    }

    本文由澳门新葡8455最新网站发布于Web前端,转载请注明出处:不错的俄罗斯方块程序代码,H5游戏开发

    关键词: