*最近,游戏“羊了个羊”风靡各平台及社交圈,在发挥游戏精神勇敢闯关的同时,我们也邀请各位技术er从技术角度来聊聊“羊了个羊”,让我们在游戏之余,探探技术之乐。
羊了个羊解决了一个什么问题?
消除 三个相同,必须能被消除 数据必须为3的倍数
1、哪些可以被点击,定义了覆盖关系 没有被覆盖的元素则可以被点击
1.1 覆盖关系 一个正方形与其他正方形是否存在交集,存在则为覆盖,不存在则为展示
2、缓存数据范围 7个,缓存范围内的数据可以被消除
实现方案
根据上述问题,我们对问题做过程分解,并提供自己的解决方案
1、生成卡片,必须为3的倍数。
入参:1、卡片种类,2、要消除的卡片组数
出参:打乱顺序的要消除的卡片集合
过程描述:生成的随机数表示为卡片种类,卡片的取值范围为卡片种类的数量,总卡片张数=要消除的卡片组数*3
代码实现
/**
* 生成随机卡片
*
* @param dispelGroupNum
* @param categorys
* @return
*/
private static int[] generatorRandomCards(int dispelGroupNum, int categorys) {
Random random = new Random();
int[] totalCardNum = new int[dispelGroupNum * 3];
for (int i = 0; i < dispelGroupNum; i++) {
int catgory = random.nextInt(categorys);
for (int j = 0; j < 3; j++) {
for (; ; ) {
int index = random.nextInt(totalCardNum.length);
if (totalCardNum[index] == 0) {
totalCardNum[index] = catgory;
break;
}
}
}
}
return totalCardNum;
}
2、覆盖关系
羊了个羊原有覆盖关系比较复杂,我们先定义一个简单的覆盖关系。
对地图设置层级,上一层级对下一层级具备覆盖关系,最上层地图全部展示。
算法1、地图定义
地图中包含了指定层级,每个层级中卡片数量是确定的.
private static List<FullList<Integer>> initCardMap() {
FullList<Integer> level0 = new FullList<>(8);
FullList<Integer> level1 = new FullList<>(9);
FullList<Integer> level2 = new FullList<>(10);
List<FullList<Integer>> cardMap = new ArrayList<>();
cardMap.add(level0);
cardMap.add(level1);
cardMap.add(level2);
return cardMap;
}
static class FullList<T> extends ArrayList {
int size;
public FullList(int size) {
this.size = size;
}
public boolean isNotFull() {
return this.size() != this.size;
}
}
算法2、将随机卡片填充到卡片地图中
/**
* 将随机卡片填充到卡片地图中
*
* @param cardMap
* @param randomCards
*/
private static void mappingRandomCardsToCardMap(List<FullList<Integer>> cardMap, int[] randomCards) {
int cardLevel = 0;
for (int key : randomCards) {
FullList<Integer> eachLevelCards = cardMap.get(cardLevel);
if (eachLevelCards.isNotFull()) {
eachLevelCards.add(key);
} else {
cardLevel++;
eachLevelCards = cardMap.get(cardLevel);
eachLevelCards.add(key);
}
}
for (List<Integer> list : cardMap) {
for (Integer card : list) {
System.out.print(card + " ");
}
System.out.println();
}
}
算法3、生成覆盖关系
我们定义个简单的覆盖关系,上一次相同角标以及+1对下一层相同角标具备覆盖关系。举个例子,2[0],2[1] 对1[0] 具有覆盖关系
/**
* 定义覆盖关系
*
* @param cardMap
* @return
*/
private static List<Node> generateConverageRelations(List<FullList<Integer>> cardMap) {
List<Node> coverageRelations = new ArrayList<>();
for (int i = 0; i < cardMap.size(); i++) {
FullList<Integer> eachLavel = cardMap.get(i);
for (int j = 0; j < eachLavel.size; j++) {
if (i + 1 < cardMap.size()) {
Node currentNode = new Node();
currentNode.level = i;
currentNode.index = j;
currentNode.category = (int) eachLavel.get(j);
Node leftNode = new Node();
leftNode.level = i + 1;
leftNode.index = j;
leftNode.category = (int) cardMap.get(leftNode.level).get(j);
currentNode.NodeList.add(leftNode);
Node rightNode = new Node();
rightNode.level = i + 1;
rightNode.index = j + 1;
rightNode.category = (int) cardMap.get(rightNode.level).get(j);
currentNode.NodeList.add(rightNode);
coverageRelations.add(currentNode);
}
}
}
return coverageRelations;
}
算法4、生成视野地图
视野地图中可以被看到,被点击
在当前算法中卡片地图的最上一层为视野地图
private static List<Node> initView(List<FullList<Integer>> cardMap) {
List<Node> viewCards = new ArrayList<>();
FullList<Integer> viewLevel = cardMap.get(cardMap.size() - 1);
for (int i = 0; i < viewLevel.size; i++) {
Node node = new Node();
node.level = cardMap.size() - 1;
node.index = i;
node.category = (int) viewLevel.get(i);
viewCards.add(node);
}
return viewCards;
}
算法5、play
5.1 定义一个缓存地图,缓存地图的数据不能超过7,超过7个时游戏结束
5.2 定义消除关系 相同种类的数据等于3个时将被消除
5.3 将选中的卡片从视野地图移动到缓存地图
private static Node moveViewCardToStorage(List<Node> viewCard, FullList<Node> playStorage, String[] commonds, Node commondNode) {
for (Node node : viewCard) {
if (node.level == Integer.parseInt(commonds[0]) && node.index == Integer.parseInt(commonds[1])) {
playStorage.add(node);
commondNode = node;
}
}
if (commondNode == null) {
System.out.println("输入的元素不存在");
} else {
viewCard.remove(commondNode);
}
List<Node> sameCategoryList = new ArrayList<>();
for (Object obj : playStorage) {
Node node = (Node) obj;
if (node.category == commondNode.category) {
sameCategoryList.add(node);
}
}
if(sameCategoryList.size()==3){
for (Node node:sameCategoryList){
playStorage.remove(node);
}
}
return commondNode;
}
5.4 重建覆盖关系,将没被覆盖的从覆盖关系中移除,添加到视野地图
private static void coverageCardToViewCard(List<Node> coverageRelations, List<Node> viewCard, Node commondNode) {
List<Node> tempCoverageNodes = new ArrayList<>();
for (Node node: coverageRelations){
if(node.NodeList.size()>0){
Node tempNode = null;
for(Node childNode:node.NodeList){
if(childNode.level==commondNode.level && childNode.index== commondNode.index){
tempNode = childNode;
}
}
if(tempNode!=null){
node.NodeList.remove(tempNode);
}
if(node.NodeList.size()==0){
tempCoverageNodes.add(node);
}
}
}
if(tempCoverageNodes.size()>0){
for (Node node:tempCoverageNodes){
coverageRelations.remove(node);
viewCard.add(node);
}
}
}
5.5定义玩的逻辑
private static void play(List<Node> coverageRelations, List<Node> viewCard) {
FullList<Node> playStorage = new FullList<>(7);
Scanner scanner = new Scanner(System.in);
for (; ; ) {
show(viewCard, playStorage, coverageRelations);
String line = scanner.nextLine();
try {
String[] commonds = line.split(",");
Node commondNode = null;
commondNode = moveViewCardToStorage(viewCard, playStorage, commonds, commondNode);
if(!playStorage.isNotFull()){
System.out.println("Game Over");
break;
}
coverageCardToViewCard(coverageRelations, viewCard, commondNode);
if(viewCard.size()==0){
System.out.println("恭喜挑战成功");
}
} catch (Exception e) {
System.out.println("输入不合法");
}
}
}
源代码
https://gitee.com/ghash/TxtGame.git
game为简单实现版本
game1实现了根据位置覆盖
js版本 http://game.syj.cool/ 欢迎大家试玩