点数
每张牌都有一个对应的点数
1-9的万、条、筒对应1-9点,字牌为10点。
吃碰杠
可以碰、杠,不可以吃。
庄家
第一局随机选庄。赢家坐庄。荒庄后庄家不变。
漏碰
可以碰牌的时候不碰,则在自己摸牌前不可以再碰这张牌。
但是对其他牌没有影响,如果漏碰了3万,那么对4万是不会有影响的。
杠牌
明杠:分为摸杠和点杠。
摸杠:先碰,后杠。
点杠:手上有3张一样的牌,其他玩家打出一样牌,选择开杠。
暗杠:手上有4张一样的牌,选择开杠,为暗杠。
抢杠:被抢杠胡算点炮,抢杠只能抢自摸明杠,被抢的杠就不再是杠。
杠上花:杠后补的牌刚好是自己要胡的那张牌,算自摸。
听牌
听牌的时候,听的牌中,至少要有一张牌是大于等于6点。
听牌后不能换牌。
听牌时打出去的这张牌不显示牌面,此牌也不可以被其他玩家碰杠胡。
听牌后自动摸打。
听牌后杠牌
听牌后仍然可以杠牌,即使杠后听的牌改变了也没有关系,只要杠后听的牌中至少有一张是大于等于6点就行。
胡牌规则
1、一局只能有一位胡牌者,如果点炮的时候有多家可胡,则逆时针方向离点炮者最近的优先胡牌。
2、牌的点数为1、2的不能胡牌,点数为3、4、5的只能自摸,点数为6、7、8、9、10的可以接炮胡和自摸胡。
例:
牌型
平胡: 1分
七对: 2分
豪华七对: 4分
清一色: 2分
一条龙: 2分
十三幺: 2分
胡牌点数:胡的那张牌的点数。
总分= 胡分 + 杠分
胡牌得分 = (胡牌点数 + 庄闲(庄5、闲0)) x 胡牌方式(接炮胡1,自摸2) x 牌型分
如果同时满足多个牌型,算分时只取其中分数最大的那个,但是界面上需要显示多个牌型。
胡牌算分
未听牌者点炮:只有点炮者输分
赢家得分 = (公式) x (人数 - 1) 输家1得分 = - (公式) x (人数 - 1) 输家2得分 = 0 输家3得分 = 0
听牌者点炮:3家都要输分
赢家得分 = (公式) + (公式) +(公式) 输家1得分 = -(公式) 输家2得分 = -(公式) 输家3得分 = -(公式)
自摸胡牌:3家都要输分
赢家得分 = (公式) + (公式) +(公式) 输家1得分 = -(公式) 输家2得分 = -(公式) 输家3得分 = -(公式)
杠牌算分
摸杠: 9 + 9 + 9, 赢27分,3家输分,每家输9分。
点杠:
未听牌者: 9x3=27,赢27分,1家输分,输27分。
听牌者: 9 + 9 + 9, 赢27分,3家输分,每家输9分。
暗杠: 9x2 + 9x2 + 9x2, 赢54分,3家输分,每家输18分。
因为暗杠的分数要翻倍。
每个玩家都需要统计
自摸次数:统计自摸的次数
接炮次数:统计接炮胡和抢杠胡的次数
点炮次数:统计点炮和被抢杠胡的次数
暗杠次数:统计暗杠次数
明杠次数:统计明杠次数
字牌只能是刻子。
总分 = 胡牌分 + 杠分
参考链接:
http://hp.vector.co.jp/authors/VA046927/mjscore/mjalgorism.html
http://hp.vector.co.jp/authors/VA046927/mjscore/AgariIndex.java
http://hp.vector.co.jp/authors/VA046927/mjscore/AgariBacktrack.java
核心思想:
9张万,9张条,9张饼,4张风,3张箭,一共34种牌。
那么34种牌就至少需要6bit才能够全部保存得下。(1 2 4 8 16 32 64 …)
麻将的手牌有14张,14张 * 6bit = 84bit
麻将的所有组合大约有1700万种,所以需要的内存为 84bit * 1700万 = 175MB 左右。
所以需要进行优化:
例如:
222456万345678饼北北,可以编码为30111011111102(三张相同牌,三张连续牌,六张连续牌,两张相同牌,中间隔开)。
下一步是将其二进制化,采用如下特制规则:
1→0
2→110
3→11110
4→1111110
10→10
20→1110
30→111110
40→11111110
很容易看出,这样编码后每张牌只占用1到2位空间,最恶情况子下(十四张不连单牌)仅占用27位。跟之前的84位相比,单组数据压缩了三分之二以上。更牛逼的是,和牌表从1700万种具体组合下降到仅仅9362种形状排列!
需要倒过来看,对照上面的规则从右往左看:
2 –> 0x7 –> 0111
23 –> 0xFB –> 1111 1011
32 –> 0xEF –> 1110 1111
233 –> 0x1F7B –> 0001 1111 0111 1011
2303 –> 0x3EFB –> 0011 1110 1111 1011
21110330 –> 0x1F7A3 –> 0001 1111 0111 1010 0011
package main
import (
"errors"
"fmt"
"net/http"
"net/rpc"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
type Arith int
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
func (t *Arith) Divide(args *Args, quo *Quotient) error {
if args.B == 0 {
return errors.New("divide by zero")
}
quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
return nil
}
func main() {
arith := new(Arith)
rpc.Register(arith)
rpc.HandleHTTP()
err := http.ListenAndServe(":1234", nil)
if err != nil {
fmt.Println(err.Error())
}
}
package main
import (
"fmt"
"log"
"net/rpc"
"os"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
func main() {
if len(os.Args) != 2 {
fmt.Println("Usage: ", os.Args[0], "server")
os.Exit(1)
}
serverAddress := os.Args[1]
client, err := rpc.DialHTTP("tcp", serverAddress+":1234")
if err != nil {
log.Fatal("dialing:", err)
}
args := Args{16, 8}
var reply int
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)
var quot Quotient
err = client.Call("Arith.Divide", args, ")
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem)
}
$ go run Client.go localhost
Arith: 16*8=128
Arith: 16/8=2 remainder 0
https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/08.4.md
事情是这样的,前两天同事找到我帮忙看个bug。
在一个C++的类中,增加了一个新的成员函数funcA,结果程序就Crash掉。但是诡异的地方就在于Crash的地方并不在funcA这个函数中,而是在另外一个函数中的一个成员变量memB(memB是一个结构体),可新增的funcA这个函数完全没有修改过memB这个成员变量。
思路:
其实这非常明显就是内存越界了,在funcA修改内存的时候,越界了,修改到了memB的内存了。
那么为什么funcA修改的内存会影响到memB呢?那就要简单说下C++的内存了。
class Student
{
funcA()
funcB()
private:
int memA;
int memB;
int memC;
};
可以看下上面这个伪代码,在这个类中,memA,memB,memC中的内存都是并排的。
所以如果在funcA中修改了memA的内容,结果由于代码bug导致内存越界了,就有可能修改到memB,这样就会出现明明没有动过memB,程序却在memB的地方Crash掉。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "getopt.h"
int main(int argc, char ** argv)
{
int c;
while ((c = getopt(argc, argv, "i:p:abc:d:012")) != -1)
{
printf("c = %c\n", c);
int this_option_optind = optind ? optind : 1;
switch (c)
{
case 'i':
{
char acIP[128] = { 0 };
strcpy(acIP, optarg);
printf("acIP = %s\n", acIP);
break;
}
case 'p':
{
int port = atoi(optarg);
printf("port = %d\n", port);
break;
}
case '0':
case '1':
case '2':
break;
case 'a':
break;
case 'b':
break;
case 'c':
printf("option c with value %s\n", optarg);
break;
case 'd':
printf("option d with value %s\n", optarg);
break;
default:
printf("Unknown option.\n");
break;
}
}
if (optind < argc)
{
printf("non-option ARGV-elements: ");
while (optind < argc)
{
printf("%s ", argv[optind++]);
}
printf("\n");
}
return 0;
}
https://github.com/alex85k/wingetopt
https://en.wikipedia.org/wiki/Getopt
文件–>首选项–>设置–>用户设置
{
"files.trimTrailingWhitespace": true,
"typescript.check.npmIsInstalled": false,
"terminal.integrated.shell.windows": "C:\\Program Files\\Git\\bin\\bash.exe",
"terminal.integrated.shellArgs.windows": [
"--login",
"--init-file",
"C:\\Program Files\\Git\\etc\\profile"
],
"terminal.external.windowsExec":"C:\\Program Files\\Git\\bin\\bash.exe"
}