add game core

This commit is contained in:
wzdwc
2020-04-04 00:10:29 +08:00
parent 530282d636
commit 6512e984a7
31 changed files with 477 additions and 119 deletions
+4 -4
View File
@@ -1,22 +1,22 @@
import {Application} from 'egg';
import { Application } from 'egg';
import * as path from 'path';
import ElkTransport from './app/helper/logTransport';
export default (app: Application) => {
app.beforeStart(async () => {
});
app.use(async function (ctx, next) {
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
if (err.name === "UnauthorizedError") {
if (err.name === 'UnauthorizedError') {
ctx.status = 401;
ctx.body = {
code: '999999',
data: {},
message: 'invalid token...',
};
ctx.logger.error('invalid token...', err)
ctx.logger.error('invalid token...', err);
}
}
});
+11 -11
View File
@@ -1,7 +1,7 @@
import BaseController from "../../lib/baseController";
import {controller, inject, post, provide} from "midway";
import {IAccountService} from '../../interface/IAccountService'
import {IAccountInfo} from "../../interface/IAccountInfo";
import BaseController from '../../lib/baseController';
import { controller, inject, post, provide } from 'midway';
import { IAccountService } from '../../interface/IAccountService';
import { IAccountInfo } from '../../interface/IAccountInfo';
@provide()
@controller('/node/user/')
@@ -15,26 +15,26 @@ export class Account extends BaseController {
try {
const { body } = this.getRequestBody();
const { userAccount, password } = body;
const accountInfo: IAccountInfo = {userAccount, password};
const accountInfo: IAccountInfo = { userAccount, password };
const result = await this.service.login(accountInfo);
this.success(result)
this.success(result);
} catch (e) {
this.ctx.logger.error('login-----:', e);
this.fail(e)
this.fail(e);
}
};
}
@post('/register')
async register() {
try {
const { body } = this.getRequestBody();
const { userAccount, password, nickName } = body;
const accountInfo: IAccountInfo = {userAccount, password, nickName};
const accountInfo: IAccountInfo = { userAccount, password, nickName };
const result = await this.service.register(accountInfo);
this.success(result)
this.success(result);
} catch (e) {
this.ctx.logger.error('login-----:', e);
this.fail(e)
this.fail(e);
}
}
}
+2 -2
View File
@@ -21,7 +21,7 @@ export class RoomController extends BaseController {
this.success(result);
} catch (e) {
this.fail('create room error');
console.log(e)
console.log(e);
}
}
@@ -33,7 +33,7 @@ export class RoomController extends BaseController {
this.success({ hasRoom: result });
} catch (e) {
this.fail('create room error');
console.log(e)
console.log(e);
}
}
}
+2 -2
View File
@@ -25,8 +25,8 @@ export class UserController extends BaseController {
const user = this.user.findByAccount(userInfo.userAccount);
this.success(user);
} catch (e) {
console.log(e)
this.fail('server error')
console.log(e);
this.fail('server error');
}
}
}
+57
View File
@@ -0,0 +1,57 @@
export interface IPlayer {
counter: number;
position: number;
userName: string;
}
enum ECommand {
CALL = 'call',
ALL_IN = 'allin',
RAISE = 'raise',
CHECK = 'check',
FOLD = 'fold',
}
export class Player {
handCard: string[] = [];
position: number = 0;
counter: number = 0;
// commandRecord: Array<string> = [];
constructor(config: IPlayer = { counter: 0, position: 0, userName: '' }) {
this.counter = config.counter;
this.position = config.position;
}
/**
* player action
* @param {string} commandString
* @param {number} prevSize
* @example action('command:raise:10')
*/
action(commandString: string, prevSize: number) {
const commandArr = commandString.split(':');
const command = commandArr[1];
let size = 0;
// player raise,get the raise size
if (command === ECommand.RAISE) {
size = Number(command[2]);
this.counter -= size;
}
if (command === ECommand.ALL_IN) {
size = this.counter;
this.counter -= size;
}
if (command === ECommand.CALL) {
size = prevSize;
this.counter -= prevSize;
}
if (command === ECommand.CHECK || command === ECommand.FOLD) {
size = 0;
}
return size;
}
}
+61
View File
@@ -0,0 +1,61 @@
export interface IPoker {
init(): void;
getCard(): string;
getRandom(number: number): number;
}
/**
* Created by jorky on 2020/2/23.
*/
export class Poker implements IPoker {
pokers: string[] = [];
constructor() {
this.init();
}
init() {
const size = [
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm' ];
const color = [ 1, 2, 3, 4 ];
for (const i of size) {
for (const j of color) {
this.pokers.push(`${i}:${j}`);
}
}
}
getCard(): string {
if (this.pokers.length === 0) return 'done';
const currCardIndex = this.getRandom(this.pokers.length);
const currCard = this.pokers[currCardIndex];
this.pokers.splice(currCardIndex, 1);
return currCard;
}
getRandom(number: number): number {
const maxNumber = Math.ceil(number);
return Math.floor(Math.random() * maxNumber);
}
}
// let poker = new Poker()
// let arr = []
// for(let i = 0; i< 53; i++) {
// arr.push(poker.getCard())
// }
// console.log(arr)
+96
View File
@@ -0,0 +1,96 @@
/**
* Created by jorky on 2020/2/23.
*/
import { Poker } from './Poker';
import { Player, IPlayer } from './Player';
interface IPokerGame {
users: IPlayer[];
playerSize: number;
}
enum EGameStatus {
GAME_READY,
GAME_START,
GAME_OVER,
}
class PokerGame {
commonCard: string[] = [];
fireCards: string[] = [];
players: Player[] = [];
poker = new Poker();
pot = 0;
status: EGameStatus = EGameStatus.GAME_READY;
currPlayer = new Player();
hasStraddle = false;
winner = new Player();
playerSize: number = 0;
constructor(config: IPokerGame) {
this.playerSize = config.playerSize;
this.init(config.users);
}
init(users: IPlayer[]) {
this.getPlayer(users);
}
play() {
this.status = EGameStatus.GAME_START;
this.getHandCard();
console.log('player: ', ...this.players);
// 烧卡
this.fireCards.push(this.poker.getCard());
// 翻牌圈
this.flop();
console.log('flop commonCard:', this.commonCard.join(','));
// 烧卡
this.fireCards.push(this.poker.getCard());
// turn
this.commonCard.push(this.poker.getCard());
console.log('turn commonCard:', this.commonCard.join(','));
// 烧卡
this.fireCards.push(this.poker.getCard());
// river
this.commonCard.push(this.poker.getCard());
console.log('river commonCard:', this.commonCard.join(','));
}
flop() {
for (let i = 0; i < 3; i++) {
this.commonCard.push(this.poker.getCard());
}
}
compareCard(cards: string[]) {
}
getPlayer(users: IPlayer[]) {
users.forEach(u => {
const player = new Player(u);
this.players.push(player);
});
}
getWinner() {
// only one player, others fold
if (this.players.length === 1) {
this.winner = this.players[0];
this.winner.counter += this.pot;
}
if (this.status === EGameStatus.GAME_OVER) {
this.winner.counter += this.pot;
}
}
getHandCard() {
for (let i = 0; i < 2; i++) {
this.players.forEach(p => {
p.handCard.push(this.poker.getCard());
});
}
}
}
console.log(PokerGame);
+174
View File
@@ -0,0 +1,174 @@
const POKER_STR = 'abcdefghijklm';
function sort(cards: string []): string[] {
let temp = '';
// 排序
for (let i = 0; i < cards.length; i++) {
for (let j = i + 1; j < cards.length; j++) {
if (cards[i] > cards[j]) {
temp = cards[i];
cards[i] = cards[j];
cards[j] = temp;
}
}
}
return cards;
}
interface IPokerStyle {
init(): void;
isStraight(str?: string): number;
}
export class PokerStyle implements IPokerStyle {
cards: string[] = [];
flushObj = {
1: [],
2: [],
3: [],
4: [],
};
straightArr: string[] = [];
pokerStyle: string[] = [ '0', '0', '0', '0', '0', '0', '0', '0' ];
numObj: Map<string, number> = new Map(
POKER_STR.split('').map(m => [ m, 0 ]));
constructor(commonCards: string[], handCards: string[]) {
const cards = [ ...commonCards, ...handCards ];
this.cards = sort(cards);
this.init();
}
init() {
let i = 0;
const isTwo = [];
const isThree = [];
let isFour = '0';
let isFullHouse = '0';
let isStraightFlush = '0';
let isFlush = [];
let isRoyalFlush = '0';
let isThreeKind = '';
let isTowPair = '';
let isPair = '';
const highCard = [];
while (i < this.cards.length) {
const color = this.cards[i][1];
const num = this.cards[i][0];
this.straightArr.push(this.cards[i][0]);
this.flushObj[color].push(num);
let value = this.numObj.get(num) || 0;
value++;
this.numObj.set(num, value);
i++;
}
// find flush
for (const f in this.flushObj) {
if (this.flushObj[f].length >= 5) {
// flush is order,so flush[length - 1] is max flush card
isFlush = this.flushObj[f];
}
}
// find two,three,four
for (const [ key, value ] of this.numObj) {
// high card
if (value === 1) {
highCard.unshift(key);
}
// pairs max count 3, source is small to large
if (value >= 2) {
isTwo.unshift(key);
}
// three of kind max count 2
if (value === 3) {
isThree.unshift(key);
}
// four of kind only one
if (value === 4) {
isFour = key;
}
}
// straight flush
if (isFlush.length !== 0 && this.isStraight(isFlush) > -1) {
if (this.isStraight(isFlush) === 8) {
isRoyalFlush = '1';
this.pokerStyle[0] = isRoyalFlush;
return;
}
isStraightFlush = '1' + this.isStraight(isFlush);
this.pokerStyle[1] = isStraightFlush;
return;
}
// four of kind
if (isFour !== '0') {
isFour += highCard[0];
this.pokerStyle[2] = isFour;
return;
}
// full house
if (isThree.length > 0 && isTwo.length > isThree.length || isThree.length === 2) {
const maxTwoCard = isThree.length === 2 ? isThree[1] : isThree[0] === isTwo[0] ? isTwo[1] : isTwo[0];
const maxThree = isThree[0];
isFullHouse = maxThree + maxTwoCard;
this.pokerStyle[3] = isFullHouse;
return;
}
// three of kind
if (isThree.length > 0) {
isThreeKind = isThree.join('');
isThreeKind += highCard[0] + highCard[1];
this.pokerStyle[4] = isThreeKind;
return;
}
// tow pair
if (isTwo.length >= 2) {
isTowPair = isTwo.join('');
// third tow pair card big then high card
const highCardForTowPair = isTwo[3] && isTwo[3] > highCard[0] ? isTwo[3] : highCard[0];
isTowPair += highCardForTowPair;
this.pokerStyle[5] = isTowPair;
return;
}
// pair
if (isTwo.length === 1) {
isPair = isTwo.join('');
isPair += highCard[0] + highCard[1] + highCard[2];
this.pokerStyle[6] = isPair;
return;
}
// High card
this.pokerStyle[7] = highCard.join('');
}
isStraight(str?: string): number {
const straightStr = str || [ ...new Set(this.straightArr) ].join('');
let first = -1;
let second = -1;
let three = -1;
if (straightStr.length === 5) {
return POKER_STR.indexOf(straightStr);
}
if (straightStr.length === 6) {
first = POKER_STR.indexOf(straightStr.slice(0, 4));
second = POKER_STR.indexOf(straightStr.slice(1, 5));
return Math.max(first, second);
}
if (straightStr.length === 7) {
first = POKER_STR.indexOf(straightStr.slice(0, 4));
second = POKER_STR.indexOf(straightStr.slice(1, 5));
three = POKER_STR.indexOf(straightStr.slice(2, 6));
return Math.max(first, second, three);
}
return -1;
}
getPokerWeight() {
return this.pokerStyle.join('');
}
}
+2 -2
View File
@@ -38,7 +38,7 @@ class LogFormat extends Map {
* @param logInfo
* @param level 日志级别
*/
public formatFetchInfoMsg(logInfo: Array<any>, level: LoggerLevel) {
public formatFetchInfoMsg(logInfo: any[], level: LoggerLevel) {
const collect = logInfo[0];
this.logField = {
fetchConsumeTime: 0,
@@ -57,7 +57,7 @@ class LogFormat extends Map {
this.logField.message = collect;
this.logField.requestTime = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
this.logField.level = level;
this.logField.stack = level === 'ERROR' ? logInfo[1] : ''
this.logField.stack = level === 'ERROR' ? logInfo[1] : '';
} else {
this.logField.timestamp = collect.startTime;
this.logField.requestTime = moment(collect.startTime).format('YYYY-MM-DD HH:mm:ss');
+1 -1
View File
@@ -27,7 +27,7 @@ class NspController extends Controller {
const nsp = app.io.of('/socket');
const message = ctx.args[0] || {};
const { room } = socket.handshake.query;
const rooms = [room];
const rooms = [ room ];
try {
const { payload } = message;
nsp.adapter.clients(rooms, (err: any, clients: any) => {
+4 -4
View File
@@ -1,7 +1,7 @@
import {Context} from "midway";
import { Context } from 'midway';
import { ITickMsg } from '../../../interface/ITickMsg';
export default function auth(): any{
export default function auth(): any {
return async (ctx: Context, next: () => Promise<any>) => {
const socket = ctx.socket as any;
const id = socket.id;
@@ -15,7 +15,7 @@ export default function auth(): any{
// 检查房间是否存在,不存在则踢出用户
// 备注:此处 app.redis 与插件无关,可用其他存储代替
const hasRoom = await roomService.findByRoomNumber(room);
function tick(id: Number, msg: ITickMsg, nsp: any, socket: any) {
function tick(id: number, msg: ITickMsg, nsp: any, socket: any) {
// 踢出用户前发送消息
socket.emit(id, ctx.helper.parseMsg('deny', msg));
// 调用 adapter 方法踢出用户,客户端触发 disconnect 事件
@@ -31,7 +31,7 @@ export default function auth(): any{
function updatePlayer(roomNumber: string, message: string, nsp: any) {
// 在线列表
nsp.adapter.clients([roomNumber], (err: any, clients: any) => {
nsp.adapter.clients([ roomNumber ], (err: any, clients: any) => {
// 更新在线用户列表
nsp.to(roomNumber).emit('online', {
clients,
+1 -1
View File
@@ -9,7 +9,7 @@ import { Context, EggAppConfig } from 'egg';
export default function elkLogger(options: EggAppConfig['elkLogger']): any {
return async (ctx: Context, next: () => Promise<any>) => {
const { match, enable } = options;
console.log('jwt', ctx.jwt)
console.log('jwt', ctx.jwt);
// 是否符合配置规则
if (match(ctx) && enable) {
ctx.setLogCollection('fetchStart', Date.now());
+1 -1
View File
@@ -1,4 +1,4 @@
import { Application } from 'midway'
import { Application } from 'midway';
export default function (app: Application) {
app.io.of('/socket').route('exchange', app.io.controller.nsp.exchange);
+3 -3
View File
@@ -65,7 +65,7 @@ export default (appInfo: EggAppInfo) => {
// jsonwebtoken 插件配置
config.jwt = {
secret: "123456",
secret: '123456',
enable: true,
match(ctx: Context) {
const reg = /login|register/;
@@ -77,9 +77,9 @@ export default (appInfo: EggAppInfo) => {
config.io = {
namespace: {
'/socket': {
connectionMiddleware: ['auth'],
connectionMiddleware: [ 'auth' ],
packetMiddleware: [],
}
},
},
redis: {
host: '127.0.0.1',
+1 -1
View File
@@ -1,5 +1,5 @@
import { EggAppConfig, PowerPartial } from 'egg';
import {Context} from "midway";
import { Context } from 'midway';
export default () => {
const config: PowerPartial<EggAppConfig> = {};
+4 -4
View File
@@ -14,14 +14,14 @@ const plugin: EggPlugin = {
enable: true,
package: 'egg-socket.io',
},
jwt:{
jwt: {
enable: true,
package: "egg-jwt",
package: 'egg-jwt',
},
mysql:{
mysql: {
enable: true,
package: 'egg-mysql',
}
},
};
export default plugin;
-1
View File
@@ -3,4 +3,3 @@ export interface IAccountInfo {
password: string;
nickName?: string;
}
+2 -2
View File
@@ -1,5 +1,5 @@
import {IAccountInfo} from "./IAccountInfo";
import {ILoginResult} from "./ILoginResult";
import { IAccountInfo } from './IAccountInfo';
import { ILoginResult } from './ILoginResult';
export interface IAccountService {
login(accountInfo: IAccountInfo): Promise<ILoginResult>;
+2 -2
View File
@@ -1,6 +1,6 @@
export interface IGameRecord {
roomId:string;
userId:string;
roomId: string;
userId: string;
commonCard: string;
handCards: string;
status: string;
+1 -1
View File
@@ -1,3 +1,3 @@
export interface ILoginResult {
token:string
token: string;
}
+1 -1
View File
@@ -1,7 +1,7 @@
export enum ResultCode {
SUCCESS = '000000',
FAILD = '100000',
UNAUTH = '999999'
UNAUTH = '999999',
}
export interface IResult {
+1 -1
View File
@@ -4,7 +4,7 @@ export interface IRoom {
export interface IRoomService {
findById(uid: string): Promise<IRoom>;
findByRoomNumber(roomNumber: number): Promise<boolean>
findByRoomNumber(roomNumber: number): Promise<boolean>;
add(): Promise<any>;
// join(roomNumber: string, userName: string): void;
// leave(roomNumber: string, message: string): void;
+1 -1
View File
@@ -1,4 +1,4 @@
export interface ITickMsg {
type: string,
type: string;
message: string;
}
+7 -7
View File
@@ -1,6 +1,6 @@
import { inject, Context } from 'midway';
import { IRequestBody } from '../interface/IRequestBody';
import {IResult, ResultCode} from "../interface/IResult";
import { IResult, ResultCode } from '../interface/IResult';
export default class BaseController {
@@ -25,10 +25,10 @@ export default class BaseController {
public success(data: any) {
const result: IResult = {
code: ResultCode.SUCCESS,
data: data,
message: 'successful'
}
this.ctx.body = result
data,
message: 'successful',
};
this.ctx.body = result;
}
/**
* 错误回调封装
@@ -38,8 +38,8 @@ export default class BaseController {
const result: IResult = {
code: ResultCode.FAILD,
data: {},
message
message,
};
this.ctx.body = result
this.ctx.body = result;
}
}
-1
View File
@@ -42,5 +42,4 @@ export default class BaseService {
}
}
}
+3 -3
View File
@@ -44,7 +44,7 @@ export class AccountService extends BaseService implements IAccountService {
try {
const hasUser = await this.checkHasUser(accountInfo.userAccount);
if (!hasUser) {
let result = await this.user.addUser(accountInfo);
const result = await this.user.addUser(accountInfo);
if (result.affectedRow === 1) {
resolve('user create successful');
}
@@ -60,7 +60,7 @@ export class AccountService extends BaseService implements IAccountService {
public async authUser(accountInfo: IAccountInfo) {
const user: IUser = await this.checkHasUser(accountInfo.userAccount);
let valid = user.password === accountInfo.password;
const valid = user.password === accountInfo.password;
if (!valid) {
throw 'incorrect user account or password.';
}
@@ -72,7 +72,7 @@ export class AccountService extends BaseService implements IAccountService {
}
private getToken(userAccount: string) {
const token = this.jwt.sign({ userAccount: userAccount },
const token = this.jwt.sign({ userAccount },
this.jwtConfig.secret, { expiresIn: 60 * 60 });
this.ctx.logger.info(`AccountService getToken token--${token}`);
return token;
+7 -7
View File
@@ -1,5 +1,5 @@
import BaseService from '../lib/baseService';
import {Context, inject, provide, plugin} from "midway";
import { Context, inject, provide, plugin } from 'midway';
import { IRoom } from '../interface/IRoom';
@provide('RoomService')
@@ -15,23 +15,23 @@ export class RoomService extends BaseService {
redis: any;
async findById(uid: string): Promise<IRoom> {
return await this.mysql.get('room', { id: uid});
return await this.mysql.get('room', { id: uid });
}
async findByRoomNumber(number: string): Promise<boolean> {
const roomNumber = await this.redis.get(`room:${number}`);
console.log(roomNumber, 'redis', number);
return !!roomNumber
return !!roomNumber;
}
async add(expires: number = 3600) {
const number = Math.floor(Math.random() * (1000000 - 100000)) + 100000;
const result = await this.mysql.insert(`room`, { room_number: number });
const result = await this.mysql.insert('room', { room_number: number });
const roomRedis = await this.redis.set(`room:${number}`, `${number}`, 'ex', expires);
if(result.affectedRows === 1 && roomRedis === 'OK') {
return { roomNumber: number }
if (result.affectedRows === 1 && roomRedis === 'OK') {
return { roomNumber: number };
} else {
throw 'room add error'
throw 'room add error';
}
}
}
+6 -6
View File
@@ -1,10 +1,10 @@
import {Context, inject, provide, plugin} from "midway";
import { Context, inject, provide, plugin } from 'midway';
import { IUser } from '../interface/IUser';
import { IUserService } from '../interface/IUserService';
import { IAccountInfo } from '../interface/IAccountInfo';
@provide('UserService')
export class UserService implements IUserService{
export class UserService implements IUserService {
@inject()
ctx: Context;
@@ -12,19 +12,19 @@ export class UserService implements IUserService{
@plugin()
mysql: any;
async findById(uid: string): Promise<IUser>{
return await this.mysql.get('user', { id: uid});
async findById(uid: string): Promise<IUser> {
return await this.mysql.get('user', { id: uid });
}
async findByAccount(account: string) {
return await this.mysql.get('user', { account: account});
return await this.mysql.get('user', { account });
}
async addUser(accountInfo: IAccountInfo): Promise<any> {
return await this.mysql.insert('user', {
account: accountInfo.userAccount,
password: accountInfo.password,
nick_name: accountInfo.nickName
nick_name: accountInfo.nickName,
});
}
-50
View File
@@ -1,50 +0,0 @@
/* tslint:disable */
const { app, assert } = require('midway-mock/bootstrap');
/* tslint:enable */
describe('test/app/controller/home.test.ts', () => {
it('should assert', async () => {
const pkg = require('../../../package.json');
assert(app.config.keys.startsWith(pkg.name));
// const ctx = app.mockContext({});
// await ctx.service.xx();
});
it('should POST /ocrAuth', () => {
/* tslint:disable */
return app.httpRequest().
post('/node/ocrAuth').
set('Authorization', 'Bearer ad7bc5ae-e19e-4d39-9a93-aa75bc01ede1').
send({
'params': JSON.stringify({
'head': {
'clientId': 0,
'appUDID': '',
'appVersion': '',
'channelId': '',
'innerMedia': '',
'outerMedia': '',
'subClientId': '0',
'systemVersion': 'undefined',
'origin': '',
},
'body': {
'name': '蔡建帮',
'address': '测试',
'authOrg': '测试政府',
'gender': '男',
'idNo': '445222198911111111',
'nation': '中国',
'validDate': '20190909 - 20990907',
'frontImage': '',
'backImage': '',
'productId': 'L0003',
'channelType': 1,
},
}),
}).
expect(200);
});
});
+21
View File
@@ -0,0 +1,21 @@
/* tslint:disable */
import {PokerStyle} from '../../../src/app/core/PokerStyle'
interface IPokerStyle {
init(): void;
isStraight(str?: string): number;
}
describe('test/app/core/pokerStyle.test.ts', () => {
//
it('full house', async () => {
console.log(PokerStyle)
let pokerStyle: IPokerStyle = new PokerStyle(['a1','b2', 'c3', 'b4', 'd1'], ['a2', 'b3'])
console.log(pokerStyle)
});
it('tow full house', async () => {
console.log(PokerStyle)
let pokerStyle: IPokerStyle = new PokerStyle(['a1','b2', 'a3', 'b4', 'd1'], ['a2', 'b3'])
console.log(pokerStyle)
});
});
+1
View File
@@ -15,6 +15,7 @@
"strict": true,
"strictPropertyInitialization": false,
"stripInternal": true,
"suppressImplicitAnyIndexErrors": true,
"target": "ES2018"
},
"exclude": [