Attack vs Defense와 누가 승자입니까? [닫은]


12

모바일에서 새로운 간단한 게임을 만드는 과정에 있으며 다음 부분에서 며칠을 보냈습니다.

간단히하기 위해 두 명의 전투기가 있다고 가정하겠습니다. 그들의 유일한 속성은 Attack and Defence입니다. 처음 공격 할 때 중요한 것은 그를 공격하고 상대방을 방어하는 것입니다. 그 반대.

그들은 장비, 아이템, 체력 또는 건강이 없습니다. 그냥 공격 대 방어.

예:

  • 파이터 1 :

    공격 : 50, 방어 : 35

  • 파이터 2 :

    공격 20, 방어 : 80

전투 과정은 단 한 번의 공격으로 승자를 결정합니다. 따라서 여러 번의 공격이나 라운드가 없습니다. 결정적으로 만들고 싶지 않지만 예기치 않은 라이트 버전을 추가하십시오. 낮은 공격력을 가진 전투기는 높은 방어력을 가진 다른 전투기를 이길 수 있습니다 (물론 항상 그렇지는 않습니다)

첫 번째 아이디어는 선형으로 만들고 균일 난수 생성기를 호출하는 것이 었습니다.

If Random() < att1 / (att1 + def2) {
    winner = fighter1
} else {
    winner = fighter2
} 

공격 50과 방어 80의 예를 들면 공격 전투기는 약 38 %의 승리를 거둘 것입니다. 그러나 예상치 못한 것이 너무 멀고 최악의 전투기가 많이 이길 것 같습니다.

비슷한 상황에서 어떻게 일했는지 궁금합니다.

추신 : 나는이 QnA와 다른 출처에서 많이 검색했으며 비슷한 질문이 SE에 비해 너무 광범위하다고 언급했습니다. 그러나 그것들은 너무 복잡하게 만들 수있는 많은 속성, 무기, 아이템, 클래스 등을 가지고 있습니다. 내 버전이 SE의 QnA 스타일에 맞게 훨씬 간단하다고 생각합니다.


1
찾고있는 사례는 무엇입니까? 공격 및 방어를위한 어떤 범위의 값을보고 있으며 그 범위의 두 숫자는 고정 된 결과를 가져야합니까? 예를 들어, 공격력이 10 인 전투기는 방어력 90에서 전투기를 물리 칠 수 있습니까?
Niels

@ user2645227 범위가 1과 400 사이라고 말할 수 있습니다. 아니요, 결정적인 결정을 원하지 않고 방어 400에서 1을 공격 할 가능성을 주지만 실제로는 거의 없습니다.
Tasos

1
따라서 Att (min) -def (max) 및 Att (max) -def (min)를 취하면 -400에서 +400까지 800의 범위를 제공합니다. 임의의 범위가 전체 범위를 덮기를 원할 것입니다. 방어-공격은 승리하기 위해 필요한 임계 값의 형태로 스케일링 마진을 제공합니다. 이것은 무작위성을 조금 줄여야합니다. 결과를 더욱 중앙 집중화하려면 원하는 곡선에 도달 할 때까지 Philipps 예제를 사용하거나 아무 곳에서나 바이올린을 사용할 수 있습니다.
Niels

답변:


24

전투 결과가 더 예측 가능하지만 완전히 결정적이지 않게하려면 최대 n 개의 시스템을 갖추십시오 .

전투 n시간을 반복하고 ( n수평이 고르지 않은 곳 ) 전투원을 더 자주이기는 승자를 선언하십시오. n놀랍게도 얻는 손실이 적을수록 가치가 커집니다 .

const int FIGHT_REPETITONS = 5 // best 3 of 5. Adjust to taste.

int fighter1wins = 0;
int fighter2wins = 0;

for (int i = 0; I < FIGHT_REPETITONS; I++) {

    If (Random() < att1 / (att1 + def2)) {
        fighter1wins++;
    } else {
        fighter2wins++;
    } 

}

If (fighter1wins > fighter2wins) {
    winner = fighter1
} else {
    winner = fighter2
} 

이 시스템은 싸움이 승리 또는 패배의 단순한 이진 결과 인 특수한 경우에만 작동합니다. 승리가 얼마나 근접했는지에 따라 승자가 여전히 일부 체력을 잃는 경우와 같이 전투에서 더 복잡한 결과가 나오면이 방법은 더 이상 작동하지 않습니다. 보다 일반적인 해결책은 난수 생성 방식을 변경하는 것입니다. 여러 개의 난수를 생성 한 다음 평균을 취하면 결과가 범위 중앙 부근에 군집화되고 더 극단적 인 결과는 더 드물게 나타납니다. 예를 들면 다음과 같습니다.

double averagedRandom3() {
    return (Random() + Random() + Random()) / 3.0;
}

다음과 같은 분포 곡선이 있습니다.

3d20 / 3의 분포

(사진 제공 : anydice- 탁상용 게임뿐만 아니라 임의성을 포함하는 게임 메커니즘 공식을 설계하는 데 유용한 도구)

현재 프로젝트에서 임의의 샘플 크기를 설정할 수있는 도우미 기능을 사용하고 있습니다.

double averagedRandom(int averageness) {
     double result = 0.0;
     for (var i = 0; i < averageness; i++) {
         result += Random();
     }
     return result / (double)averageness;
}

더 나은 접근법으로 보입니다. 하나의 질문. averagedRandom3 () 함수에서 +대신 사용해야 *합니까 , 아니면 무엇을 오해해야합니까?
Tasos

@Tasos 예, *가 아닌 + 여야합니다. 또한 여러 샘플을 곱하는 임의 함수가 있습니다. 이것은 낮은 값에 대한 강한 바이어스를 갖는 난수 함수를 제공하며 일부 상황에서도 유용 할 수 있습니다.
Philipp

1
1-2 일 동안 질문을 공개하고 다른 답변이없는 경우 귀하의 질문을 선택하겠습니다. 나는 그것을 찬성했지만 마음에 들지 않으면 다른 대답을 할 기회를주고 싶습니다.
Tasos

P :이 대답은 이미 대답으로 마킹이 응답 자격을하게 충분한 표를 얻을 생각
함자 하산

1
일부 사람들이 다른 접근법을 생각해 내면 궁금합니다. 한 사람이이 답변을 하향 투표했습니다. 아마도 그들은 대안을 제공하고 싶을 수도 있습니다.
Philipp

8

이것이 내가 정복 이모 테이터 애플릿에서 전투의 승자를 결정하는 데 사용한 것입니다. 이 게임에서는 상황과 유사하게 공격 가치와 방어 가치 만 있습니다. 공격자가 이길 확률은 공격자가 가질 수있는 점수가 높을수록 방어 점수가 더 많을수록 더 많으며, 동일한 값으로 공격이 성공할 확률은 50 %입니다.

연산

  1. 임의의 동전을 뒤집습니다.

    1a. 머리 : 방어는 점수를 잃습니다.

    1b. 꼬리 : 머리가 포인트를 잃습니다.

  2. 방어와 공격자 모두 여전히 점수가 있다면 1 단계로 돌아가십시오.

  3. 0 점 이하의 사람은 전투에서 패합니다.

    3a. 0까지 공격자 : 공격이 실패합니다.

    3b. 0까지 방어 : 공격 성공

Java로 작성했지만 다른 언어로 쉽게 번역 할 수 있어야합니다.

Random rnd = new Random();
while (att > 0 && def > 0)
{
    if (rnd.nextDouble() < 0.5)
        def--;
    else
        att--;
}
boolean attackSucceeds = att > 0;

예를 들어, 확률이 50 %인지 확인하기 위해 att = 2 및 def = 2라고 가정하겠습니다.

전투는 최대 n = att + def - 1동전 뒤집기 또는이 예에서는 3 으로 결정됩니다 (기본적으로 최고 3입니다). 2 개의 가능한 코인 플립 조합이 있습니다. 여기서 "W"는 공격자가 코인 플립을 이겼 음을 의미하고 "L"은 공격자가 코인 플립을 잃은 것을 의미합니다.

L,L,L - Attacker loses
L,L,W - Attacker loses
L,W,L - Attacker loses
L,W,W - Attacker wins
W,L,L - Attacker loses
W,L,W - Attacker wins
W,W,L - Attacker wins
W,W,W - Attacker wins

공격자는 4/8 또는 50 %의 경우에 이깁니다.

수학

이 간단한 알고리즘에서 발생하는 수학적 확률은 알고리즘 자체보다 복잡합니다.

정확히 x L이있는 조합의 수는 조합 함수에 의해 제공됩니다.

C(n, x) = n! / (x! * (n - x)!)

L 0att - 1L 사이에 있으면 공격자가 이깁니다 . 당첨 조합의 수 는 누적 이항 분포 를 0통한 조합의 합과 att - 1같습니다.

    (att - 1)
w =     Σ     C(n, x)
      x = 0

침입자의 확률 승리는 (2)에 의해 분할 된 N , 누적 이항 확률 :

p = w / 2^n

여기서, 임의의 확률이 계산에서 자바 코드 attdef값 :

/**
 * Returns the probability of the attacker winning.
 * @param att The attacker's points.
 * @param def The defense's points.
 * @return The probability of the attacker winning, between 0.0 and 1.0.
 */
public static double probWin(int att, int def)
{
    long w = 0;
    int n = att + def - 1;
    if (n < 0)
        return Double.NaN;
    for (int i = 0; i < att; i++)
        w += combination(n, i);

    return (double) w / (1 << n);
}

/**
 * Computes C(n, k) = n! / (k! * (n - k)!)
 * @param n The number of possibilities.
 * @param k The number of choices.
 * @return The combination.
 */
public static long combination(int n, int k)
{
    long c = 1;
    for (long i = n; i > n - k; i--)
        c *= i;
    for (long i = 2; i <= k; i++)
        c /= i;
    return c;
}

테스트 코드 :

public static void main(String[] args)
{
    for (int n = 0; n < 10; n++)
        for (int k = 0; k <= n; k++)
            System.out.println("C(" + n + ", " + k + ") = " + combination(n, k));

    for (int att = 0; att < 5; att++)
        for (int def = 0; def < 10; def++)
            System.out.println("att: " + att + ", def: " + def + "; prob: " + probWin(att, def));
}

산출:

att: 0, def: 0; prob: NaN
att: 0, def: 1; prob: 0.0
att: 0, def: 2; prob: 0.0
att: 0, def: 3; prob: 0.0
att: 0, def: 4; prob: 0.0
att: 1, def: 0; prob: 1.0
att: 1, def: 1; prob: 0.5
att: 1, def: 2; prob: 0.25
att: 1, def: 3; prob: 0.125
att: 1, def: 4; prob: 0.0625
att: 1, def: 5; prob: 0.03125
att: 2, def: 0; prob: 1.0
att: 2, def: 1; prob: 0.75
att: 2, def: 2; prob: 0.5
att: 2, def: 3; prob: 0.3125
att: 2, def: 4; prob: 0.1875
att: 2, def: 5; prob: 0.109375
att: 2, def: 6; prob: 0.0625
att: 3, def: 0; prob: 1.0
att: 3, def: 1; prob: 0.875
att: 3, def: 2; prob: 0.6875
att: 3, def: 3; prob: 0.5
att: 3, def: 4; prob: 0.34375
att: 3, def: 5; prob: 0.2265625
att: 3, def: 6; prob: 0.14453125
att: 3, def: 7; prob: 0.08984375
att: 4, def: 0; prob: 1.0
att: 4, def: 1; prob: 0.9375
att: 4, def: 2; prob: 0.8125
att: 4, def: 3; prob: 0.65625
att: 4, def: 4; prob: 0.5
att: 4, def: 5; prob: 0.36328125
att: 4, def: 6; prob: 0.25390625
att: 4, def: 7; prob: 0.171875
att: 4, def: 8; prob: 0.11328125

관찰

확률은 0.0공격자가 0포인트를 가지고 있는 경우, 공격자가 포인트를 1.0가지고 있지만 방어에는 0포인트가있는 0.5경우, 포인트가 같으면 0.5공격자가 방어보다 적은 포인트를 가지고있는 경우 보다 적고 공격자가 방어보다 0.5더 많은 포인트를 가지고있는 경우 보다 크다 .

촬영 att = 50하고 def = 80, 나는로 전환하는 데 필요한 BigDecimal피하기 오버 플로우의,하지만 난 0.0040 정도의 확률을 얻을.

att값을 attdef값 의 평균으로 변경하여 확률을 0.5에 가깝게 만들 수 있습니다 . Att = 50, Def = 80은 (65, 80)이되어 0.1056의 확률을 산출합니다.


1
또 다른 흥미로운 접근법. 이 알고리즘은 쉽게 시각화되어 매우 흥미로울 수 있습니다.
Philipp

5

정규 분포에서 샘플링 한 난수로 공격을 수정할 수 있습니다. 이런 방식으로 대부분의 경우 결과는 예상 한대로이지만 때때로 높은 공격은 낮은 방어에서 패배하거나 낮은 공격은 높은 방어에서 승리합니다. 이런 일이 일어날 확률은 공격과 방어의 차이가 클수록 작아 질 것입니다.

if (att1 + norm(0, sigma) - def2 > 0) {
  winner = fighter1;
}
else {
  winner = fighter2;
}

이 함수 norm(x0, sigma)는 표준 편차 시그마를 사용하여 x0을 중심으로하는 정규 분포에서 샘플링 된 부동 소수점을 반환합니다. 대부분의 프로그래밍 언어는 라이브러리에 이러한 기능을 제공하지만 직접 만들고 싶다면 이 질문을 살펴보십시오 . 시그마가 '정확하게'느껴지도록 조정해야하지만 10-20의 값이 시작하기에 좋은 장소 일 수 있습니다.

몇 시그마 값의 경우 주어진 승리 확률은 att1 - def2다음과 같습니다. 승리 확률


정규 분포 값에는 실제 범위가 없으므로 게임에서 정규 분포 무작위 값을 사용할 때 매우 극단적 인 값이 생성 될 가능성은 없지만 불가능하지 않은 상황을 피하기 위해 결과를 고정하는 것이 합리적입니다. 게임을 중단시킬 수 있습니다.
Philipp
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.