Les examens de fin d'études des débutants devraient développer le "Blackjack"
Inspiré de l'article "Un examen de fin d'études d'un débutant en programmation devrait développer 'Blackjack'", j'ai essayé de créer un blackjack en Java. Étonnamment difficile ... La faible compétence de conception est perceptible. La difficulté de mise en œuvre était juste. Le temps de montage est-il d'environ 3 heures?
J'aimerais le garder propre, alors veuillez le signaler.
Le code source a été téléchargé sur github
.
https://github.com/akiyu33333/blackjack
Les spécifications sont l'article original de Qiita ou github README Veuillez vous y référer.
~~ * Refactorisé en réponse à l'indication. ~~ De plus, nous avons mis en place un refactoring et un calcul de score.
22/06 postscript J'ai apporté d'autres corrections en réponse au point de @ watashi_da. On m'a conseillé d'avoir un indicateur CPU, et à la suite de la refactorisation, j'ai finalement décidé de diviser les classes en ** User ** et ** Dealer **.
Main.java
Main.java
import blackjackgame.BlackJackGame;
public class Main {
public static void main(String[] args) {
new BlackJackGame().start();
}
}
BlackJackGame.java Nom de classe et nom de package modifiés.
BlackJackGame.java
package blackjackgame;
import card.Deck;
import player.Dealer;
import player.AbstractPlayer;
import player.User;
public class BlackJackGame {
/**
*début du jeu
*/
public void start() {
System.out.println("★ ☆ ★ ☆ ★ ☆ ★ ☆ ★ ☆ ★ ☆ Bienvenue au Blackjack! ★ ☆ ★ ☆ ★ ☆ ★ ☆ ★ ☆ ★ ☆\n");
System.out.println("Commencer le jeu.\n");
Deck deck = new Deck();
AbstractPlayer user = new User("tu");
AbstractPlayer dealer = new Dealer("Marchand");
user.initCardList(deck);
dealer.initCardList(deck);
user.drawCard(deck);
if(!user.isBust()) dealer.drawCard(deck);
printGameResult(user, dealer);
System.out.println("\n Le Blackjack est terminé! Veuillez rejouer ★");
}
/**
*Afficher les résultats du jeu
* @param player1 player1
* @param player2 player 2
*/
private void printGameResult(AbstractPlayer player1, AbstractPlayer player2) {
if (player1.calcScore() == player2.calcScore()) {
System.out.println("C'est un tirage au sort.");
return;
}
AbstractPlayer winner = !player1.isBust() && (player2.isBust() || player1.calcScore() > player2.calcScore())
? player1
: player2;
System.out.println( winner.getName() + "Est le gagnant!" );
}
}
~~Player.java~~ → AbstractPlayer.java
J'étais conscient de ne générer de «lombok» que là où c'était nécessaire.
Au début, j'en ai fait une classe abstraite, mais comme je n'en ai plus besoin, j'en ai fait une classe concrète.
Dois-je créer une interface et l'implémenter?
~~ getPoint ()
~~ J'ai personnellement ressenti la croissance de pouvoir écrire le lambda de calcScore ()
(rires)
Renommé «getPoint» en «calcScore». J'ai oublié ce qui suit du code lisible.
De nombreux programmeurs sont habitués à la convention selon laquelle les méthodes commençant par get sont des "accesseurs légers" qui ne renvoient que les valeurs des membres. Le non-respect de ces conditions peut être trompeur.
Puisque le processus de calcul est inclus ici, getXXX
n'est pas bon.
Ajout d'une logique de calcul pour A. La méthode consistait à calculer autre que A, puis à calculer à partir du nombre de A et à additionner.
22/06 postscript Puisqu'il s'agit d'une classe abstraite, le nom est également modifié.
AbstractPlayer.java
package player;
import card.Card;
import card.Deck;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
public abstract class AbstractPlayer {
protected static final int BUST_POINT = 21;
@Getter
private final String name;
private List<Card> cardList = new ArrayList<>();
@Getter
@Setter
private boolean isBust = false;
public AbstractPlayer(String name) {
this.name = name;
}
private void addCardList(Card card){
cardList.add(card);
}
public int calcScore(){
int score = cardList.stream().filter(card -> card.getPoint() > 1 ).mapToInt(card -> card.getPoint()).sum();
int aceCardCount = (int) cardList.stream().filter(card -> card.getPoint() == 1 ).count();
if (aceCardCount == 0) return score;
int borderScore = 11 - aceCardCount;
return score > borderScore ? score + aceCardCount : score + 10 + aceCardCount ;
}
public void draw(Deck deck) {
draw(deck,false);
}
/**
*Piochez une carte du jeu
* @pont de pont param
* @param isHidden Masquer la carte piochée
*/
public void draw(Deck deck, boolean isHidden) {
Card card = deck.draw();
addCardList(card);
if (calcScore() > BUST_POINT) setBust(true);
String msg = isHidden
? this.name + "Je ne connais pas la carte que j'ai dessinée."
: this.name + "La carte dessinée par" + card.toString() + "est.";
System.out.println( msg );
}
/**
*Créer une première main
* @pont de pont param
*/
public abstract void initCardList(Deck deck);
/**
*Piochez une carte du jeu
* @pont de pont param
*/
public abstract void drawCard(Deck deck);
}
User.java 22/06 postscript Nouvellement créé pour faire la distinction entre le lecteur et le processeur.
User.java
package player;
import card.Deck;
import java.util.Objects;
import java.util.Scanner;
public class User extends AbstractPlayer {
public User(String name) {
super(name);
}
@Override
public void initCardList(Deck deck) {
draw(deck);
draw(deck);
}
@Override
public void drawCard(Deck deck) {
System.out.println( getName() + "Le score actuel de" + calcScore() + "C'est un point.\n");
try (Scanner sc = new Scanner(System.in)) {
String line = null;
while (!isBust() && !Objects.equals(line, "N")) {
System.out.println("Tirez-vous une carte? Entrez N si vous ne voulez pas dessiner Y.");
line = sc.nextLine();
if (Objects.equals(line, "Y")) {
draw(deck);
System.out.println( getName() + "Le score actuel de" + calcScore() + "C'est un point.\n");
} else if (!Objects.equals(line, "N")) {
System.out.println("Y/Tout autre élément que N a été saisi.");
}
}
}
}
}
Dealer.java 22/06 postscript Nouvellement créé pour faire la distinction entre le lecteur et le processeur.
Dealer.java
package player;
import card.Deck;
public class Dealer extends AbstractPlayer {
public Dealer(String name) {
super(name);
}
@Override
public void initCardList(Deck deck) {
draw(deck);
draw(deck, true);
}
@Override
public void drawCard(Deck deck) {
System.out.println( getName() + "Le score actuel de" + calcScore() + "C'est un point.\n");
while (calcScore() < 17){
draw(deck);
System.out.println( getName() + "Le score actuel de" + calcScore() + "C'est un point.\n");
}
}
}
Card.java Rend le champ «final». Le "commutateur" et l'opérateur ternaire sont maladroits.
Card.java
package card;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class Card {
private final Suit suit;
private final int rank;
private String toDisplayValue() {
switch (this.rank) {
case 1:
return "A";
case 11:
return "J";
case 12:
return "Q";
case 13:
return "K";
default:
return String.valueOf(this.rank);
}
}
public int getPoint() {
return this.rank > 10 ? 10 : this.rank;
}
@Override
public String toString() {
return this.suit.getMark() + "de" + this.toDisplayValue();
}
}
Deck.java
J'ai essayé d'utiliser la méthode d'initialisation.
Lambda ici aussi se sentait bien (rires)
Je veux remplacer la valeur de retour du lambda par bill
telle quelle sans utiliser ~~new ArrayList <> ()
, mais je ne savais pas comment l'écrire. ~~
J'ai pu l'écrire en lambda, donc je l'ai corrigé.
Bien que la langue soit différente, j'ai lu cet article et j'ai pensé que je ne devrais pas utiliser foreach pour quoi que ce soit.
Utiliser forEach avec JavaScript est le dernier recours
En fait, ce n'est pas du JavaScript, donc le contenu de l'article ne peut pas être utilisé tel quel, mais j'ai été impressionné par l'idée.
Collections.shuffle
J'ai aussi appris cela pour la première fois. Il existe une méthode pratique.
Deck.java
package card;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Deck {
private List<Card> bill;
{
bill = Arrays.stream(Suit.values()).flatMap(s -> IntStream.rangeClosed(1,13).mapToObj(i -> new Card(s,i))).collect(Collectors.toList());
Collections.shuffle(bill);
}
public Card draw(){
Card card = bill.get(0);
bill.remove(0);
return card;
}
}
Suit.java
Mon «énum» préféré.
J'ai oublié d'utiliser lombok
même avec ʻenum`, alors je l'ai corrigé.
Suit.java
package card;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
public enum Suit {
SPADE("bêche"),
HEART("cœur"),
DIAMOND("diamant"),
CLUB("club");
@Getter
private final String mark;
}
était amusant. IntelliJ n'a pas de raccourci pour créer Javadoc. Puisque nous avons permis de diviser ~~ A entre 1 et 11 et ~~ A, nous aimerions prendre en charge plusieurs joueurs.