Небольшое вступление


Я попал в мир IT относительно недавно: всего два года я занимаюсь разработкой приложений под iOS. Но кроме Objective-C и Swift меня всегда манил мир Java и C#. Периодически я выделял время, чтоб посмотреть какие-то видео, обучающие основам этих языков, но дальше простого просмотра и переписывания кода с экрана дело не заходило. И тут я вспомнил об одной математической игре, которую мне однажды посоветовал мой друг.


Суть игры заключается в следующем: вы идете по улице и смотрите на автомобильные номера. И в каждом номере считаете сумму всех цифр (например, в номере 8037 нужно посчитать 8 + 0 + 3 + 7). Это самый простой уровень игры. Второй по сложности уровень — посчитать сумму первой половины номера и второй (80 + 37). Есть еще третий уровень — умножить все цифры (нули при этом пропустив: 8 х 3 х 7) и четвертый — умножить первую половину номера на вторую (80 х 37).


В общем: эту игру (в консольном варианте) я и решил написать на четырех языках: Swift, Objective-C, Java и C#. Что из этого получилось? Давайте посмотрим.


С чего начнем?


Начнем мы с создания пустых проектов под консольные приложения: для Swift и Objective-C будем использовать Xcode, для Java — естественно IntelliJ IDEA, а для C# — Xamarin Studio.


Первым делом напишем вспомогательный класс GameStart. Он у нас будет заниматься запросом ответа от пользователя пока тот не введет ключевое слово для выхода из приложения.


Swift


Создадим сам класс:


class GameStart {

}

В нем у нас будет свойство exitWord и инициализатор:


private var exitWord: String

init(with exitWord: String) {
    self.exitWord = exitWord
}

Это и будет наше ключевое слово.


Также в нем будет метод startGame который будет постоянно спрашивать у пользователя ответ, пока тот не введет слово для выхода:


func startGame() {
    print(GreetingMessage.replacingOccurrences(of: ExitWordPlaceholder, with: self.exitWord))
    guard let inputWord = readLine() else {
        print(ErrorMessage)
        return
    }

    self.check(inputWord: inputWord)
}

private func check(inputWord: String) {
    if inputWord == self.exitWord {
        print(GoodByeMessage)
    } else {
        print(InputAcceptMessage.replacingOccurrences(of: InputWordPlaceholder, with: inputWord))
        startGame()
    }
}

Как видите, метод startGame приветствует пользователя, затем считывает из командной строки то что ввел пользователь, и передает полученную строку в метод check(inputWord:).


Строковые константы, которые были использованы:


private let ExitWordPlaceholder = "{exitWord}"
private let InputWordPlaceholder = "{inputWord}"

private let GreetingMessage = "Please enter your answer (enter \"\(ExitWordPlaceholder)\" for exit):"
private let InputAcceptMessage = "You entered \"\(InputWordPlaceholder)\".\n"
private let GoodByeMessage = "Good bye.\n"
private let ErrorMessage = "There is unknown error, sorry. Good bye.\n"

Наш класс готов, теперь нужно создать объект и вызвать метод startGame():


let gameStart = GameStart(with: "quit")
gameStart.startGame()

В консоли это выглядит примерно вот так:



Теперь напишем этот же класс на Objective-C:


// файл заголовка GameStart.h
@interface GameStart : NSObject

- (instancetype)initWithExitWord:(NSString *)exitWord;
- (void)startGame;

@end

// файл реализации GameStart.m
const NSString *GreetingMessage = @"Please enter your answer (enter \"%@\" for exit):";
const NSString *InputAcceptMessage = @"You entered \"%@\".\n";
const NSString *GoodByeMessage = @"Good bye.\n";
const NSString *ErrorMessage = @"There is unknown error, sorry. Good bye.\n";

@interface GameStart()

@property (strong, nonatomic) NSString *exitWord;

@end

@implementation GameStart

- (instancetype)initWithExitWord:(NSString *)exitWord {
    self = [super init];
    if (self) {
        self.exitWord = exitWord;
    }
    return self;
}

- (void)startGame {
    NSLog(GreetingMessage, self.exitWord);
    NSString *inputWord = [self readLine];

    if (inputWord) {
        [self checkInputWord:inputWord];
    } else {
        NSLog(@"%@", ErrorMessage);
    }
}

- (void)checkInputWord:(NSString *)inputWord {
    if ([inputWord isEqualToString:self.exitWord]) {
        NSLog(@"%@", GoodByeMessage);
    } else {
        NSLog(InputAcceptMessage, inputWord);
        [self startGame];
    }
}

- (NSString *)readLine {
    char inputValue;
    scanf("%s", &inputValue);
    return [NSString stringWithUTF8String:&inputValue];
}

@end

Ну и создание объекта с вызовом метода:


GameStart *gameStart = [[GameStart alloc] initWithExitWord:@"quit"];
[gameStart startGame];

Дальше у нас на очереди Java.


Класс GameStart:


public class GameStart {

    private static final String GreetingMessage = "Please enter your answer (enter \"%s\" for exit):";
    private static final String InputAcceptMessage = "You entered \"%s\".\n";
    private static final String GoodByeMessage = "Good bye.\n";

    private String exitWord;

    public GameStart(String exitWord) {
        this.exitWord = exitWord;
    }

    void startGame() {
        System.out.println(String.format(GreetingMessage, exitWord));
        String inputWord = readLine();

        checkInputWord(inputWord);
    }

    private void checkInputWord(String inputWord) {
        if (inputWord.equals(exitWord)) {
            System.out.println(GoodByeMessage);
        } else {
            System.out.println(String.format(InputAcceptMessage, inputWord));
            startGame();
        }
    }

    private String readLine() {
        java.util.Scanner scanner = new java.util.Scanner(System.in);
        return scanner.next();
    }

}

И вызов:


GameStart gameStart = new GameStart("quit");
gameStart.startGame();

И завершает четверку лидеров C#


Класс:


public class GameStart
{
    const string GreetingMessage = "Please enter your answer (enter \"{0}\" for exit):";
    const string InputAcceptMessage = "You entered \"{0}\".\n";
    const string GoodByeMessage = "Good bye.\n";

    readonly string exitWord;

    public GameStart(string exitWord)
    {
        this.exitWord = exitWord;
    }

    public void startGame()
    {
        Console.WriteLine(string.Format(GreetingMessage, exitWord));
        string inputWord = Console.ReadLine();

        checkInputWord(inputWord);
    }

    void checkInputWord(string inputWord)
    {
        if (inputWord.Equals(exitWord))
        {
            Console.WriteLine(GoodByeMessage);
        }
        else
        {
            Console.WriteLine(string.Format(InputAcceptMessage, inputWord));
            startGame();
        }
    }
}

Вызов:


GameStart gameStart = new GameStart("quit");
gameStart.startGame();

Немного рандома не повредит


Также добавим в проект вспомогательный класс, который будет формировать наш автомобильный номер (номер должен состоять из четырех случайных цифр). Сам класс назовем просто — Randomizer.


Swift:


class Randomizer {

    var firstNumber: UInt32
    var secondNumber: UInt32
    var thirdNumber: UInt32
    var fourthNumber: UInt32

    init() {
        self.firstNumber = arc4random() % 10
        self.secondNumber = arc4random() % 10
        self.thirdNumber = arc4random() % 10
        self.fourthNumber = arc4random() % 10
    }
}

Objective-C:


// файл заголовка Randomizer.h
@interface Randomizer : NSObject

@property (assign, nonatomic) NSInteger firstNumber;
@property (assign, nonatomic) NSInteger secondNumber;
@property (assign, nonatomic) NSInteger thirdNumber;
@property (assign, nonatomic) NSInteger fourthNumber;

@end

// файл реализации Randomizer.m
@implementation Randomizer

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.firstNumber = arc4random() % 10;
        self.secondNumber = arc4random() % 10;
        self.thirdNumber = arc4random() % 10;
        self.fourthNumber = arc4random() % 10;
    }
    return self;
}

@end

Java:


public class Randomizer {

    private static Random random = new Random();

    int firstNumber;
    int secondNumber;
    int thirdNumber;
    int fourthNumber;

    public Randomizer() {
        firstNumber = random.nextInt(10);
        secondNumber = random.nextInt(10);
        thirdNumber = random.nextInt(10);
        fourthNumber = random.nextInt(10);
    }

}

C#:


public class Randomizer
{
    static readonly Random random = new Random();

    public int firstNumber;
    public int secondNumber;
    public int thirdNumber;
    public int fourthNumber;

    public Randomizer()
    {
        firstNumber = random.Next(10);
        secondNumber = random.Next(10);
        thirdNumber = random.Next(10);
        fourthNumber = random.Next(10);
    }
}

Как видите, при инициализации мы просто заполняем четыре поля в классе случайными целыми числами от 0 до 9.


Универсальность наше все


Несмотря на то, что в нашем приложении будет всего одна игра, мы сделаем вид, что предусматриваем расширяемость приложения в будущем. Поэтому добавим интерфейс (для Swift и Objective-C — протокол) Game с методами greet(with exitWord: String) и check(userAnswer: String).


Swift:


protocol Game {

    func greet(with exitWord: String)
    func check(userAnswer: String)

}

Objective-C:


@protocol Game <NSObject>

- (void)greetWithExitWord:(NSString *)exitWord;
- (void)checkUserAnswer:(NSString *)userAnswer;

@end

Java:


public interface Game {

    void greet(String exitWord);
    void checkUserAnswer(String userAnswer);

}

C#:


public interface IGame
{
    void greet(string exitWord);
    void checkUserAnswer(string userAnswer);
}

На этом первую часть я закончу. Реализацию самой игры с выбором уровня сложности, проверкой ответа игрока на корректность, с блэкджеком и ... мы сделаем во второй части. Всем добра. :)

Поделиться с друзьями
-->

Комментарии (26)


  1. pfemidi
    26.04.2017 15:49
    +5

    Я правильно понял что startGame вызывает checkInputWord, а checkInputWord при неправильном вводе вызывает startGame, а startGame вызывает checkInputWord, а checkInputWord при неправильном вводе вызывает startGame и т.д. и смысл игры в том, чтобы покрашиться по переполнению стека? :-)


    1. Adium
      26.04.2017 16:27
      +5

      image


    1. s_suhanov
      26.04.2017 16:31
      -2

      Ну да, если найдется юзер, который за один сеанс введет пару миллионов ответов подряд, то для него смысл игры будет именно в этом. :)


      1. Prototik
        27.04.2017 08:27
        +1

        Пару миллионов? В JRE размер стека по-умолчанию — 512 килобайт. Если мы возьмём ОЧЕНЬ оптимистичный расчёт в 16 байтов на фрейм (два указателя на x86_64, мизер), то в стек поместится ~ 32k вызовов, или 16k ваших чудесных итераций. Хоть этот предел и сложно достичь, но мне всё-таки кажется, что код стоит переписать.


        1. s_suhanov
          27.04.2017 08:44

          Вот с вашим комментарием по делу очень согласен. Подскажете как именно переписать?


          1. Prototik
            27.04.2017 08:49
            +1

            Ну что-то в таком духе:

                void startGame() {
                    System.out.println(String.format(GreetingMessage, exitWord));
                    while (true) {
                      String inputWord = readLine();
                      if (!checkInputWord(inputWord))
                        break;
                    }
                }
            
                private boolean checkInputWord(String inputWord) {
                    if (inputWord.equals(exitWord)) {
                        System.out.println(GoodByeMessage);
                        return false;
                    }
                    System.out.println(String.format(InputAcceptMessage, inputWord));
                    return true;
                }
            


            1. s_suhanov
              27.04.2017 10:03

              Цикл вместо рекурсии — ваша правда. Наверное, можно даже checkInputWord(inputWord) вынести в условие цикла, а перед циклом сделать String inputWord = readLine(), чтоб избежать while (true). Спасибо вам за адекватный ответ.


              1. Prototik
                27.04.2017 10:21
                +1

                Вместо дублирования readLine можно использовать цикл do {} while (); Не уверен, что аналогичные конструкции есть в других языках, но в Java есть.


                1. s_suhanov
                  27.04.2017 11:26

                  Точно. Так правильнее всего. Спасибо.


          1. tfs_cradle
            27.04.2017 09:27
            +1

            public void startGame()
            {
            while(!quit){

            }
            }


    1. s_suhanov
      26.04.2017 20:50
      -3

      Вы, кстати, свое предположение подтвердили экспериментом? Удалось добиться краша? :)


  1. Cuthbert
    27.04.2017 09:27
    +2

    Автор тот ещё тролль — серия АК (Крым), да ещё и на синем фоне (типа Евросоюз)…


    1. s_suhanov
      27.04.2017 10:08
      +1

      Это не "типа Евросоюз", это — современный украинский автомобильный номер. :)


    1. s_suhanov
      27.04.2017 10:09
      +1

      А за внимательность — вам плюсы в карму. :)


  1. sergi
    27.04.2017 14:15

    Автору кодить еще учиться. А за номерок респект.


    1. s_suhanov
      27.04.2017 14:48
      +1

      Автору кодить еще учиться

      Чем автор, собственно и занимается. :)


  1. IGR2014
    27.04.2017 21:49
    +1

    Раньше я заходил на хабр почитать классные большие статьи с кучей технических подробностей о том чего я не знал. А теперь захожу посмотреть на программку на 4-х языках, которая пока что просто выводит номер, а в дальнейшем будет складывать и умножать его числа. Мда.
    Вам конечно успеха, но практического смысла я не увидел. Совсем. Извините.


    1. fareloz
      02.05.2017 12:38

      Очень печально, что люди воспринимают Хабр как площадку исключительно для профессионалов. По мне так вариативность материала на разный вкус и уровень — это преимущество, а не недостаток.


      1. IGR2014
        05.05.2017 15:01

        Простите, а мне, в свою очередь, очень печально что Хабр воспринимают как площадку, где можно публиковать статьи, место которым на первых страницах учебников по программированию. Там где знакомство с операторами языка.


        1. s_suhanov
          05.05.2017 15:05

          Мир не идеален, а жизнь несправедлива. Да. :(


        1. fareloz
          05.05.2017 15:14

          Дело в том, что «сложность» статьи — это субъективное оценочное суждение. Всегда найдется тот, кто посчитает статью любой сложности «легкой и недостойной». Именно поэтому существуют механизмы:
          — оценочные: Вы можете проголосовать за статью негативно или позитивно
          — фильтрационные: выбрать лучшее, все подряд и так далее
          — подписки-отписки.

          Так что если Вы прочитали эту статью, то тут есть выбор из двух причин:
          — Вы не умеете фильтровать контент
          — Общее мнение хабра-сообщества положительно в противовес вашему единичному отрицательному.


    1. s_suhanov
      02.05.2017 19:02

      Я не понимаю зачем же вы заходите посмотреть на эту программку? Разве я ввел вас в заблуждение текстом до ката?


      1. IGR2014
        05.05.2017 15:04

        Если честно, то да. Из вашего названия я решил что у вас будет распознавание этих самых номеров с камеры (заходил через ВК, так что не увидел в каких хабах размещён пост).


  1. ruslanfedoseenko
    28.04.2017 08:59
    +1

    как улучшение хотел бы посоветовать хранить номер в short int(16 bit) и цифры номера получать в геттерах с помощью деления и остатков от деления. Как по мне 4 int слишком толсто для для 4 цифр


    1. BubaVV
      30.04.2017 15:48

      BCD


  1. kxl
    28.04.2017 19:19
    +1

    только за рулём не играйте…