GoodGame Империя коллекционная монета со случайными смещениями в (почти в POSIX) Баш


Сегодня, моей целью было снова, чтобы улучшить свои оболочки в POSIX возможности скриптинга.

В одну ММО игру под названием GoodGame Империя, там можно Код себе монету (налог) коллектора. Но это менее важно, чем мои намерения.

К сожалению, я не смогла избежать массивов, также это может быть сделано, но элегантный способ? Я буду рад увидеть любые предложения.

#!/bin/bash

print_usage_and_exit()
{
    echo "Usage: $0 [-1]"
    echo "      -1: One-time coin collect."
    echo "Default: Repeat coin collecting until CTRL+C is pressed."
    exit 1
}

no_repeat=false

while getopts ":1h" option
do
    case "${option}" in
        1)
          no_repeat=true
          ;;
        h | *)
          print_usage_and_exit
          ;;
    esac
done

shift $((OPTIND - 1))

# ------------------------------------------------------------------------------

# global constants and variables - self-explanatory

declare -r window_name_chrome="Goodgame Empire - Google Chrome"

declare -r screen_resolution=$(xdpyinfo | awk '/dimensions:/ {print $2}')

# we need to keep track of these two variables used by mouse_click function
previous_rand=10
operation_add=true

# ------------------------------------------------------------------------------

print_error_and_exit()
{
    # check if exactly two arguments have been passed
    test "$#" -eq 2 || print_error_and_exit 2 "print_error_and_exit(): There have not been passed exactly two arguments!"

    # check if the first argument is a number
    is_number "$1" || print_error_and_exit 3 "print_error_and_exit(): The argument #1 is not a number!"

    bold=$(tput bold)
    red=$(tput setaf 1)
    nocolor=$(tput sgr0)

    echo "$bold$red$2 Exit code = $1.$nocolor" >&2
    exit "$1"
}

# ------------------------------------------------------------------------------

is_number()
{
    # check if exactly one argument has been passed
    test "$#" -eq 1 || print_error_and_exit 4 "is_number(): There has not been passed exactly one argument!"

    # check if the argument is an integer
    test "$1" -eq "$1" 2>/dev/null
}

# ------------------------------------------------------------------------------

random_number()
{
    # check if exactly two arguments have been passed
    test "$#" -eq 2 || print_error_and_exit 5 "random_number(): There have not been passed exactly two arguments!"

    # check if the arguments are both numbers
    is_number "$1" || print_error_and_exit 6 "random_number(): The argument #1 is not a number!"
    is_number "$2" || print_error_and_exit 7 "random_number(): The argument #2 is not a number!"

    # generate one pseudo-random integer within the specified range
    shuf -i "$1-$2" -n 1
}

# ------------------------------------------------------------------------------

activate_window_via_name() {
    # check if exactly one argument has been passed
    test "$#" -eq 1 || print_error_and_exit 8 "activate_window_via_name(): There has not been passed exactly one argument!"

    xdotool search --name "$1" windowactivate --sync
}

# ------------------------------------------------------------------------------

maximize_active_window() {
    # check if no argument has been passed
    test "$#" -eq 0 || print_error_and_exit 9 "maximize_active_window(): There has been passed some argument, none expected!"

    wmctrl -r :ACTIVE: -b add,maximized_vert,maximized_horz
}

# ------------------------------------------------------------------------------

mouse_click() {
    # check if exactly two arguments have been passed
    test "$#" -eq 2 || print_error_and_exit 10 "mouse_click(): There have not been passed exactly two arguments!"

    # check if both of the arguments are numbers
    is_number "$1" || print_error_and_exit 11 "mouse_click(): The argument #1 is not a number!"
    is_number "$2" || print_error_and_exit 12 "mouse_click(): The argument #2 is not a number!"

    # 1. invert the operation_add boolean value,
    #    it seems Bash does not have inbuilt command for that
    # N: operation_add determines whether we will be adding or
    #    subtracting the random number later

    test "$operation_add" = true && operation_add=false || operation_add=true

    # 2. generate pseuso-random integer between 0 and 7, inclusive,
    #    if the generated number is the same as the previous_rand,
    #    generate until it is different
    # N: rand will be later used as pixel offset from the given coordinates

    # we define a constant for randomness

    declare -r randomness=7

    rand=$(random_number 0 "$randomness")

    while [ "$rand" -eq "$previous_rand" ]
    do
        rand=$(random_number 0 "$randomness")
    done

    # 3. we don't want to repeat clicks right with the same offset,
    #    so we store information about the previous_rand here

    previous_rand="$rand"

    # 4. depending on the boolean value of operation_add,
    #    we either add the rand, or subtract it to/from the position x/y

    if [ "$operation_add" = true ]
    then
        pos_x=$(($1 + rand))
        pos_y=$(($2 + rand))
    else
        pos_x=$(($1 - rand))
        pos_y=$(($2 - rand))
    fi

    #  activate Goodgame Empire window and wait for sync,
    #  we need to do this before each click,
    #  because the user may have clicked on some other window
    #  during the 2 second delay
    activate_window_via_name "$window_name_chrome"

    maximize_active_window

    # xdotool can move mouse and simulate button clicks and more
    # ----------------------------------------------------------
        # move the mouse cursor to the given position and wait for sync
        # click the left mouse button
        # restore the original mouse cursor position and wait for sync
        # wait for 2 seconds
    xdotool \
        mousemove --sync "$pos_x" "$pos_y" \
        click 1 \
        mousemove --sync restore \
        sleep 2
}

# ------------------------------------------------------------------------------

mouse_click_coords() {
    # accept all parameters together as one array
    local coords=("$@")

    # self-explanatory, but non-memorizable
    array_items_count="${#coords[*]}"

    # check if there have been passed exactly ten arguments
    test "$array_items_count" -eq 10 || print_error_and_exit 13 "mouse_click_coords(): There have not been passed exactly ten arguments!"

    for (( i = 0; i < "$array_items_count"; i += 2 ))
    do
        mouse_click "${coords[$i]}" "${coords[$i + 1]}"
    done
}

# ------------------------------------------------------------------------------

collect_coins_1920x1080() {
    local coords=(
        1895 955
        1104 691
        1131 660
        1145 570
        1199 381
    )

    mouse_click_coords "${coords[@]}"
}


# ------------------------------------------------------------------------------

collect_coins_3840x1080() {
    local coords=(
        3815 955
        3024 691
        3051 660
        3065 570
        3119 381
    )

    mouse_click_coords "${coords[@]}"
}

# ------------------------------------------------------------------------------

collect_coins() {
    case "$screen_resolution" in
        1920x1080) collect_coins_1920x1080
        ;;
        3840x1080) collect_coins_3840x1080
        ;;
    esac
}

# ------------------------------------------------------------------------------

if [ "$no_repeat" = false ]
then
    echo "Repeating coin collecting until CTRL+C is pressed!"

    while true
    do
        collect_coins

        # wait for 10 minutes
        sleep 600
    done
else
    echo "One-time coin collecting!"

    collect_coins
fi


149
5
задан 31 января 2018 в 07:01 Источник Поделиться
Комментарии
2 ответа

У вас уже есть большой обзор, вот дополнение.

Рассмотреть вопрос о создании errexit и nounset флаги

Вы можете сделать скрипт более надежные с

set -e -u

Оба варианта совместимы с POSIX.

Различать и непрошенный для использования

Это просто вежливость, но когда я прошу его помочь с -h или --helpЯ ожидаю, что это печать на стандартный вывод (так что я могу трубы на пейджер, если она длинная, или принтер и т. д.) и выход с состояния успех. Если я вхожу в непризнанных вариант, то я ожидаю, что выход ошибки и неудачи покинуть код.

Я предлагаю (с очевидным изменением, чтобы сделать print_usage):

case "${option}" in
1)
no_repeat=true
;;
h)
print_usage
exit 0
;;
*)
print_usage >&2
exit 1
;;
esac

Как крошечная точка, в использовании сообщения, это немного самонадеянно считать, что карты терминалов сочетание клавиш CTRL+C и на прерывание сигнала - возможно, сообщение может просто сказать "пока прервано" или "неопределенно" или похожие.

Запись для всех случаев

Есть еще один case заявление здесь:

collect_coins() {
case "$screen_resolution" in
1920x1080) collect_coins_1920x1080
;;
3840x1080) collect_coins_3840x1080
;;
esac
}

Что произойдет, если $screen_resolution не соответствует какой-либо из этих двух значений? Никаких действий, а не предупреждение. Мы могли бы добавить все *) чтобы хоть что-то делать в таком состоянии.

Более радикальный вариант-покончить с case целиком, и написать имя функции, называется:

collect_coins() {
if ! "collect_coins_$screen_resolution"
then echo "Unrecognised screen resolution" >&2; exit 1
fi
}

Для дополнительной прочности, используйте type встроенные, чтобы подтвердить, что collect_coins_$screen_resolution является функцией (а не команду $PATH) прежде чем вызвать его.

Размер экрана действительно постоянным?

Мы проверяем только после того, как размеры экрана:

declare -r screen_resolution=$(xdpyinfo | awk '/^  dimensions:/ {print $2}')

Однако, для длительной программы, это вполне может быть неправдой. Многих X-сервера может изменить свою реальную или виртуальную размер выхода в таком масштабе; пользователь может даже другой монитор (например, на ноутбук, который может быть пристыкован к большим экранам).

Отметим также, что xdpyinfo будет печатать dimensions линии для каждого экрана дисплея - может потребоваться, чтобы быть более разборчивыми.

Делает mouse_click_coords требуется ровно 5 позиций?

Это для меня не очевидно, почему эту функцию должен пять пар аргументов. Это может быть сделано в процессе пары аргументов пока у нас не кончатся:

mouse_click_coords() {
while [ $# -gt 1 ]
do
mouse_click "$1" "$2"
shift 2
done

if [ $# -eq 1 ]
then
print_error_and_exit 13 "mouse_click_coords(): coordinates must be in pairs"
fi
}

И collect_coins_* функции не нужны такие переменные:

collect_coins_1920x1080() {
mouse_click_coords \
1895 955 \
1104 691 \
1131 660 \
1145 570 \
1199 381
}

collect_coins_3840x1080() {
mouse_click_coords \
3815 955 \
3024 691 \
3051 660 \
3065 570 \
3119 381
}

2
ответ дан 1 февраля 2018 в 09:02 Источник Поделиться

Требования

Скрипт использует много нестандартных программ, чтобы делать свою работу (shuf, xdotool, ...).
Я бы добавил чек в начале, чтобы убедиться, что все зависимости присутствуют в системе.
Без этого, если что-то отсутствует, то скрипт может работать, или работают частично, что может быть более ужасного, чем сообщение сразу всем требованиям.

Управление коды ошибок

Похоже, вы пытаетесь использовать другой код ошибки для каждой другой точки отказа. Коды ошибок жестко заданы в каждой точке ошибки, и таким образом, это трудно сказать, если значения действительно уникальны. Вы можете дать разные названия кодов и определите их в верхней части файла, где его легко увидеть.

Вам действительно нужно различать условия выхода?
У вас есть другой код, который опирается на эти ценности?
Я не уверен, что это стоит.

Отчеты об ошибках

Я считаю эти предложения очень странный английский:


test "$#" -eq 2 || print_error_and_exit 5 "random_number(): There have not been passed exactly two arguments!"

И это поможет отладку включить немного больше информации о недопустимых аргументов, например:

test "$#" -eq 2 || print_error_and_exit 5 "random_number(): Expected 2 arguments, got $#: $@"

Стиль

Там есть функция декларации стили в скрипте:


random_number()
{
...
}

activate_window_via_name() {
...
}


Это хорошо использовать постоянно один стиль. Я предпочитаю второе.

Арифметические контексте

Это можно записать проще:


for (( i = 0; i < "$array_items_count"; i += 2 ))

Как это:

for (( i = 0; i < array_items_count; i += 2 ))

Альтернативой использованию массивов


К сожалению, я не смогла избежать массивов, также это может быть сделано, но элегантный способ? Я буду рад увидеть любые предложения.

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


local coords=(
1895 955
1104 691
1131 660
1145 570
1199 381
)

Вы могли бы сделать нечто подобное:

local coords="1895x955 1104x691 1131x660 1145x570 1199x381"

То есть, добавить не-белые-пробелом в пары координат, чтобы избежать слово-расщепление. Теперь вы сможете перебрать все координаты с простым for coord in $coords, а затем использовать параметр расширения синтаксиса (x=${coord%%x*}; y=${coord##*x}) для разделения значений. Это не очень элегантно, но без массивов, я думаю, что это лучшее, что вы можете сделать.

3
ответ дан 31 января 2018 в 06:01 Источник Поделиться