扎金花(搜狐笔试题)

  题目描述
  
  两个搜狐的程序员加了一个月班,终于放假了,于是他们决定扎金花渡过愉快的假期 。
  
  游戏规则:
  
  共52张普通牌,牌面为2,3,4,5,6,7,8,9,10,J,Q,K,A之一,大小递增,各四张; 每人抓三张牌。两人比较手中三张牌大小,大的人获胜。
  
  对于牌型的规则如下: 1.三张牌一样即为豹子 2.三张牌相连为顺子(A23不算顺子) 3.有且仅有两张牌一样为对子 豹子>顺子>对子>普通牌型
  在牌型一样时,比较牌型数值大小(如AAA>KKK,QAK>534,QQ2>10104)
  在二人均无特殊牌型时,依次比较三张牌中最大的。大的人获胜,如果最大的牌一样,则比较第二大,以此类推(如37K>89Q) 如二人牌面相同,则为平局。
  
  输入描述:
  
  输入两个字符串代表两个玩家的牌(如”10KQ” “354”),先输入的作为玩家1,后输入的作为玩家2
  
  输出描述:
  
  1 代表 玩家1赢 0 代表 平局 -1 代表 玩家2赢 -2 代表不合法的输入
  
  输入例子:
  
  KQ3 3Q9 10QA 6102 5810 7KK 632 74J 10102 K77 JKJ 926 68K 27A
  
  输出例子:
  

  1 1 -1 -1 1 1 -1




java版本的代码实现:

package cn.cat.algorithm;

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class GambleGoldFlower {

	/**
	 * 分析:
	 * 	  1、校验出入,数值为2—A,13种牌,每种牌四张。
	 *    2、给每种牌号都设定分值,2为2分,3为3分,以此类推,A为14分。
	 *    3、比较牌型。豹子加分3000000分,顺子加2000000分,对子加1000000分,
	 *       普通牌型则直接把三张牌对应的分值,由大到小分值顺序拼凑成一个数字,例如K52 -》 130502
	 *      
	 * 注意:
	 *    1、比较牌型时优先从大牌开始匹配,例如AAA是属于对子,但是也是豹子,应该优先匹配为大牌,即豹子。
	 *    2、优先比较牌型,牌型大的则赢,如果牌型一致或者不是特殊牌型(豹子、顺子、对子),则比较牌号的大小。
	 *      
	 * @Description: 
	 * @author gwj
	 * @Created 2018年4月12日 下午2:32:24 
	 * @param args
	 * @throws InterruptedException
	 */
	@SuppressWarnings("resource")
	public static void main(String[] args) throws InterruptedException {
		System.out.println("请输入牌型(单个空格隔开每组牌):");
		Scanner scanner = new Scanner(System.in);
		String[] part = scanner.nextLine().split(" ");
		if (part.length % 2 != 0) {
			throw new IllegalArgumentException("输入的牌局数为" + part.length + ",局数必须是偶数!");
		}
		
		System.out.println("牌型比较结果:");
		for (int i = 0; i < part.length - 1; i++) {
			System.out.print(play(part[i], part[++i]));
			System.out.print(" ");
		}
		
	}
	
	/** 开始游戏
	 * @Description: 
	 * @author gwj
	 * @Created 2018年4月16日 上午11:27:38 
	 * @param player1CardStr 玩家一的牌
	 * @param player2CardStr 玩家而的牌
	 * @return 返回游戏结果。1表示玩家1胜利,-1表示玩家2胜出,0表示平局。
	 */
	private static int play(String player1CardStr, String player2CardStr) {
		//1.判断牌号是否非法
		if (isIllegalCardNo(player1CardStr)) {
			System.out.println(player1CardStr + "是不合法的牌");
			return -2;
		}
		if (isIllegalCardNo(player2CardStr)) {
			System.out.println(player2CardStr + "是不合法的牌");
			return -2;
		}
		
		//2.分割出玩家的每张牌号
		String[] player1Card = splitCardNoAndAsc(player1CardStr);
		String[] player2Card = splitCardNoAndAsc(player2CardStr);
		

		//3.判断两个玩家的牌合在一起,看同一种牌的数量没有超过4张。
		String[] mergeCardNo = new String[]{
				player1Card[0], player1Card[1], player1Card[2],	
				player2Card[0], player2Card[1], player2Card[2]
		};
		if (hasOverFourForSameCardClass(mergeCardNo)) {
			System.out.println("两个玩家的牌有冲突,同一个牌号超过4张。");
			return -2;
		};
		
		//4.计算牌型的分值
		int player1Score = calcCardPatternScore(player1Card);
		int player2Score = calcCardPatternScore(player2Card);
		
		
		//5.比较分值
		int result;
		if (player1Score > player2Score) {
			result = 1;
		} else if (player1Score < player2Score) {
			result = -1;
		} else {
			result = 0;
		}
		
		return result;
	}
	
	/** 校验牌编号是否非法
	 * @Description: 
	 * @author gwj
	 * @Created 2018年4月13日 下午5:16:49 
	 * @param cardNo
	 * @return true表示非法
	 */
	private static boolean isIllegalCardNo(String cardNo) {
		return !cardNo.matches("([23456789JQKA]|10){3}");
	}
	
	/** 将一组牌号进行分割出每张独立的牌,并进行升序排序
	 * @Description: 
	 * @author gwj
	 * @Created 2018年4月13日 下午5:43:15 
	 * @param cardNo 牌号
	 * @return 返回一个数组,一个元素表示一张牌。
	 */
	private static String[] splitCardNoAndAsc(String cardNo) {
		String[] cardNos = new String[3];
		char[] charArray = cardNo.toCharArray();
		if (cardNo.length() == 3) {
			//没有出现10的牌号,故直接分解为一个字符就是一个牌号
			cardNos[0] = String.valueOf(charArray[0]);
			cardNos[1] = String.valueOf(charArray[1]);
			cardNos[2] = String.valueOf(charArray[2]);
		} else {
			int charIndex = 0;
			int resultInex = 0;
			while (charIndex < charArray.length) {
				if (charArray[charIndex] == '1' && charArray[charIndex + 1] == '0') {
					//出现10的牌号,则一次获取两个字符,组成10。
					cardNos[resultInex] = String.valueOf(charArray, charIndex, 2);
					charIndex ++;
				} else {
					cardNos[resultInex] = String.valueOf(charArray[charIndex]);
				}
				//循环累加
				charIndex ++;
				resultInex ++;
			}
		}
		
		//将牌号转换为分值,便于进行排序。
		int[] scores = calcCardNoScore(cardNos);
		//将牌分值按顺序进行升序排序,使用冒泡法。
		for (int i = 0; i < 3; i++) {
			for (int j = 0; j < 3 - i - 1; j++) {
				if (scores[j] > scores[j+1]) {
					//交换分值数组
					int tempScore = scores[j];
					scores[j] = scores[j+1];
					scores[j+1] = tempScore;
					
					//同时修改牌号数组顺序
					String tempCardNo = cardNos[j];
					cardNos[j] = cardNos[j+1];
					cardNos[j+1] = tempCardNo;
				}
			}
		}
	
		return cardNos;
	}
	
	/** 相同的牌种是否有超过4张。
	 * @Description: 
	 * @author gwj
	 * @Created 2018年4月13日 下午5:42:06 
	 * @param cardNos
	 * @return true表示有超过4张同种牌。
	 */
	private static boolean hasOverFourForSameCardClass(String[] cardNos) {
		Map<String, Integer> cardCountMap = new HashMap<String, Integer>();
		for (String cardNo : cardNos) {
			Integer count = cardCountMap.get(cardNo) == null ? 0 : cardCountMap.get(cardNo);
			//当前牌种数量加1
			count ++;
			//同种牌数量超过4张
			if (count > 4) {
				return true;
			}
			cardCountMap.put(cardNo, count);
		}
		
		return false;
	}
	
	/** 将牌号转换为分值
	 * @Description: 
	 * @author gwj
	 * @Created 2018年4月16日 上午11:08:19 
	 * @param cardNos
	 * @return
	 */
	private static int[] convertToScoreForCardNo(String[] cardNos) {
		//将牌型转换为牌分值
		int len = cardNos.length;
		int[] scores = new int[len];
		for (int i = 0; i < len; i++) {
			scores[i] = calcCardNoScore(cardNos[i]);
		}
		return scores;
	}
	
	/** 升序排序
	 * @Description: 
	 * @author gwj
	 * @Created 2018年4月16日 上午11:11:50 
	 * @param arr
	 * @return 返回排序后的arr参数数组
	 */
	private static int[] orderAsc(int[] arr) {
		int len = arr.length;
		//将牌分值按顺序进行升序排序,使用冒泡法。
		for (int i = 0; i < len; i++) {
			for (int j = 0; j < len - i - 1; j++) {
				if (arr[j] > arr[j+1]) {
					int temp = arr[j];
					arr[j] = arr[j+1];
					arr[j+1] = temp;
				}
			}
		}
		//返回当前数组
		return arr;
	}
	
	
	/** 计算牌号分值
	 * @Description: 
	 * @author gwj
	 * @Created 2018年4月13日 下午5:55:24 
	 * @param cardNos 牌号集合
	 * @return 牌号分值集合
	 */
	private static int[] calcCardNoScore(String[] cardNos) {
		int[] score = new int[cardNos.length];
		for (int i = 0; i < score.length; i++) {
			score[i] = calcCardNoScore(cardNos[i]);
		}
		return score;
	}
		
	/** 计算牌号分值
	 * @Description: 
	 * @author gwj
	 * @Created 2018年4月13日 下午5:55:24 
	 * @param cardNo 牌号
	 * @return 牌号分值
	 */
	private static int calcCardNoScore(String cardNo) {
		int score = 0;
		if (cardNo.matches("\\d{1,2}")) {
			score += Integer.valueOf(cardNo);
		} else {
			if (cardNo.equals("J")) {
				score += 11;
			} else if (cardNo.equals("Q")) {
				score += 12;
			} else if (cardNo.equals("K")) {
				score += 13;
			} else if (cardNo.equals("A")) {
				score += 14;
			}
		}
		return score;
	}
	
	
	/** 计算牌型分值
	 * @Description: 
	 * @author gwj
	 * @Created 2018年4月13日 下午5:55:24 
	 * @param cardNos 集合
	 * @return 牌型分值。如果不是特殊牌型((豹子、顺子、对子),则返回0。
	 */
	private static int calcCardPatternScore(String[] cardNos) {
		int score = 0;
		if (isLeopardCardPattern(cardNos)) {
			//豹子,三张牌号都一样,随便拿取一个元素作为加分牌号。
			score = 3000000 + calcCardNoScore(cardNos[2]);
		} else if (isSequenceCardPattern(cardNos)) {
			//顺子,直接给最大牌号进行加分。
			score = 2000000 + calcCardNoScore(cardNos[2]);
		} else if (isPairCardPattern(cardNos)) {
			//对子,对子比较特殊,直接找出对子牌号来加分。
			String pairCardNo = cardNos[0];
			if (cardNos[1].equals(cardNos[2])) {
				pairCardNo = cardNos[1];
			}
			score = 1000000 + calcCardNoScore(pairCardNo);
		} else {
			//普通排序,由大牌到小牌按顺序拼接分值。
			int[] scores = calcCardNoScore(cardNos);
			String scoreSum = "";
			for (int i = scores.length -1 ; i >= 0; i--) {
				if (scores[i] < 10) {
					//一个数的分值,增加0来补齐两位数。
					scoreSum = scoreSum + "0" + scores[i];
				} else {
					scoreSum = scoreSum + scores[i];
				}
			}
			score = Integer.parseInt(scoreSum);
		}
		return score;
	}
	
	/** 是否豹子牌型
	 * @Description: 
	 * @author gwj
	 * @Created 2018年4月16日 上午9:51:48 
	 * @param cardNos 牌号集
	 * @return true表示豹子。
	 */
	private static boolean isLeopardCardPattern(String[] cardNos) {
		//豹子牌型:三个牌号一样。
		return cardNos[0].equals(cardNos[1]) && cardNos[1].equals(cardNos[2]);
	}
	
	/** 是否对子牌型
	 * @Description: 
	 * @author gwj
	 * @Created 2018年4月16日 上午9:54:09 
	 * @param cardNos
	 * @return true表示对子。
	 */
	private static boolean isPairCardPattern(String[] cardNos) {
		//对子牌型:存在有两张一样牌号的牌。
		return cardNos[0].equals(cardNos[1]) 
				|| cardNos[0].equals(cardNos[2])
				|| cardNos[1].equals(cardNos[2]);
	}
	
	/** 是否顺子牌型
	 * @Description: 
	 * @author gwj
	 * @Created 2018年4月16日 上午9:54:09 
	 * @param cardNos
	 * @return true表示顺子。
	 */
	private static boolean isSequenceCardPattern(String[] cardNos) {
		//转换为分值并排序
		int[] score1 = orderAsc(convertToScoreForCardNo(cardNos));
		//如果是顺子,则分值是每次加1。
		return (score1[0] + 1 == score1[1]) && (score1[1] + 1 == score1[2]);
	}
	

}



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值