มีข้อความ 'if' มากเกินไป


263

รหัสต่อไปนี้ใช้งานได้ตามที่ฉันต้องการ แต่มันก็น่าเกลียดเกินไปหรืออย่างอื่น ฉันดูสูตรและพยายามเขียนวิธีแก้ปัญหาสองสามข้อ แต่ท้ายที่สุดก็มีข้อความคล้ายกันจำนวนหนึ่ง

มีสูตรคณิตศาสตร์ชนิดหนึ่งที่จะเป็นประโยชน์กับฉันในกรณีนี้หรือเป็น 16 ถ้าข้อความสั่งยอมรับ?

เพื่ออธิบายรหัสมันเป็นเกมประเภทหันกลับพร้อมกัน .. ผู้เล่นสองคนมีปุ่มการกระทำสี่ปุ่มแต่ละปุ่มและผลลัพธ์มาจากอาร์เรย์ (0-3) แต่ตัวแปร 'หนึ่ง' และ 'สอง' สามารถเป็นได้ มอบหมายอะไรก็ได้ถ้าสิ่งนี้ช่วยได้ ผลลัพธ์คือ 0 = ไม่ชนะ 1 = p1 ชนะ 2 = p2 ชนะ 3 = ชนะทั้งคู่

public int fightMath(int one, int two) {

    if(one == 0 && two == 0) { result = 0; }
    else if(one == 0 && two == 1) { result = 0; }
    else if(one == 0 && two == 2) { result = 1; }
    else if(one == 0 && two == 3) { result = 2; }
    else if(one == 1 && two == 0) { result = 0; }
    else if(one == 1 && two == 1) { result = 0; }
    else if(one == 1 && two == 2) { result = 2; }
    else if(one == 1 && two == 3) { result = 1; }
    else if(one == 2 && two == 0) { result = 2; }
    else if(one == 2 && two == 1) { result = 1; }
    else if(one == 2 && two == 2) { result = 3; }
    else if(one == 2 && two == 3) { result = 3; }
    else if(one == 3 && two == 0) { result = 1; }
    else if(one == 3 && two == 1) { result = 2; }
    else if(one == 3 && two == 2) { result = 3; }
    else if(one == 3 && two == 3) { result = 3; }

    return result;
}


9
มีเหตุผลบางอย่างที่นี่ที่สามารถวางนัยทั่วไปมากกว่าที่จะถูกบังคับเดรัจฉาน? มีฟังก์ชั่นบางอย่างf(a, b)ที่ให้คำตอบในกรณีทั่วไปหรือไม่? คุณยังไม่ได้อธิบายตรรกะของการคำนวณดังนั้นคำตอบทั้งหมดเป็นเพียงแค่ลิปสติกบนหมู ฉันจะเริ่มต้นด้วยการคิดใหม่อย่างจริงจังตรรกะโปรแกรมของคุณการใช้intค่าสถานะสำหรับการกระทำนั้นล้าสมัยไปมาก enums สามารถมีเหตุผลและมีความหมายซึ่งจะช่วยให้คุณสามารถเขียนรหัสของคุณในลักษณะที่ทันสมัย
Boris the Spider

หลังจากอ่านคำตอบ @Steve Benett ที่ให้ไว้ในคำถามสำรองของเขาที่ลิงก์ด้านบนฉันสามารถสรุปได้ว่าไม่มีคำตอบสูตรส่งตรงถึงสิ่งนี้เนื่องจากเป็นหลักเหมือนกับฐานข้อมูล ฉันพยายามที่จะอธิบายในคำถามเดิมที่ฉันสร้างเกมง่ายๆ (นักสู้) และผู้ใช้มีปุ่มให้เลือก 4 ปุ่ม: blockHigh (0), blockLow (1), โจมตีสูง (2) และโจมตีต่ำ (3) ตัวเลขเหล่านี้จะถูกเก็บไว้ในอาร์เรย์จนจำเป็น หลังจากนั้นพวกเขาจะใช้ฟังก์ชั่น 'fightMath ()' ซึ่งเรียกการเลือกของผู้เล่นกับผู้เล่นหนึ่งคนเพื่อให้ได้ผลลัพธ์ ไม่มีการตรวจจับการชนจริง
TomFirth

9
หากคุณมีคำตอบโปรดโพสต์เช่นนี้ การอภิปรายเพิ่มเติมในความคิดเห็นเป็นการยากที่จะติดตามโดยเฉพาะอย่างยิ่งเมื่อมีส่วนร่วมของรหัส หากคุณต้องการพูดคุยเกี่ยวกับคำถามที่ว่าควรจะถูกโยกย้ายไปยังการตรวจสอบรหัสหรือไม่มีการอภิปรายMetaเกี่ยวกับเรื่องนั้น
George Stocker

1
"เหมือนกับฐานข้อมูล" หมายความว่าอย่างไร หากค่าเหล่านี้อยู่ในฐานข้อมูลให้ดึงค่าจากที่นั่น ไม่อย่างนั้นถ้ามันซับซ้อนจริง ๆ ฉันจะปล่อยให้มันเหมือนคุณมีมันและเพิ่มความเห็นตรรกะทางธุรกิจหลังจากแต่ละบรรทัดเพื่อให้ผู้คนเข้าใจว่าเกิดอะไรขึ้น จะดีกว่า (สำหรับฉัน) ยาวนานและชัดเจน - ใครบางคนในอนาคตสามารถเข้าใจสิ่งที่เกิดขึ้น หากคุณใส่ไว้ในแผนที่หรือพยายามบันทึกรหัส 8 บรรทัดส่วนกลับด้านมีขนาดเล็กมากและ downsize ใหญ่กว่า: คุณทำให้สับสนมากขึ้นเรื่อย ๆ สำหรับคนที่ต้องการอ่านรหัสของคุณในวันเดียว
skaz

คำตอบ:


600

หากคุณไม่สามารถคิดสูตรได้คุณสามารถใช้ตารางสำหรับผลลัพธ์จำนวน จำกัด ดังกล่าว:

final int[][] result = new int[][] {
  { 0, 0, 1, 2 },
  { 0, 0, 2, 1 },
  { 2, 1, 3, 3 },
  { 1, 2, 3, 3 }
};
return result[one][two];

7
สิ่งนี้น่าสนใจเพราะฉันไม่เคยเห็นวิธีนี้มาก่อน ฉันไม่แน่ใจว่าฉันเข้าใจผลตอบแทน แต่จะสนุกกับการทดสอบนั้น
TomFirth

4
คุณไม่จำเป็นต้องมีการยืนยัน Java จะโยนIndexOutOfBoundsExceptionต่อไปหากดัชนีอย่างน้อยหนึ่งรายการไม่อยู่ในขอบเขต
JAB

43
@JoeHarper หากคุณต้องการอ่านอะไรง่าย ๆ คุณจะไม่ใช้หมายเลขเวทย์มนตร์ในตอนแรกและคุณจะมีความคิดเห็นอธิบายการทำแผนที่ ตามที่ฉันต้องการรุ่นนี้เพื่อต้นฉบับ แต่สำหรับสิ่งที่รักษาได้ในระยะยาวฉันจะใช้วิธีการที่เกี่ยวข้องกับประเภทที่ระบุหรืออย่างน้อยค่าคงที่ชื่อ
JAB

13
@ JoeHarper "ในทางทฤษฎี" เป็นสิ่งหนึ่ง "ในทางปฏิบัติ" เป็นอีกสิ่งหนึ่ง แน่นอนฉันพยายามใช้การตั้งชื่อแบบอธิบาย (ยกเว้นi/ j/ kอนุสัญญาสำหรับตัวแปรลูป), ค่าคงที่ชื่อ, การจัดรหัสของฉันในรูปแบบที่อ่านได้ ฯลฯ แต่เมื่อชื่อตัวแปรและฟังก์ชันเริ่มใช้อักขระเกิน 20 ตัว แต่ละอันที่ฉันพบมันทำให้รหัสที่อ่านได้น้อยลง วิธีการตามปกติของฉันคือพยายามหารหัสที่เข้าใจง่าย แต่สั้นกระชับพร้อมความคิดเห็นที่นี่และมีเพื่ออธิบายว่าทำไมรหัสจึงมีโครงสร้างแบบที่เป็น (เทียบกับวิธี) การใส่เหตุผลว่าทำไมในชื่อก็แค่ตัดทุกอย่างออกไป
JAB

13
ฉันชอบวิธีนี้สำหรับปัญหาเฉพาะนี้เพราะผลลัพธ์ถูกกำหนดโดยเมทริกซ์ผลลัพธ์
ลองใช้

201

เนื่องจากชุดข้อมูลของคุณมีขนาดเล็กคุณสามารถบีบอัดทุกอย่างเป็นจำนวนเต็ม 1 ยาวและเปลี่ยนเป็นสูตรได้

public int fightMath(int one,int two)
{
   return (int)(0xF9F66090L >> (2*(one*4 + two)))%4;
}

ตัวแปร bitwise เพิ่มเติม:

สิ่งนี้ทำให้การใช้ความจริงทุกอย่างเป็นทวีคูณของ 2

public int fightMath(int one,int two)
{
   return (0xF9F66090 >> ((one << 3) | (two << 1))) & 0x3;
}

ต้นกำเนิดของเวทมนตร์คงที่

ฉันจะว่าอย่างไรได้? โลกต้องการเวทมนตร์บางครั้งความเป็นไปได้ของสิ่งที่เรียกร้องให้มีการสร้าง

สาระสำคัญของฟังก์ชั่นที่แก้ปัญหาของ OP คือแผนที่จากตัวเลข 2 ตัว (หนึ่ง, สอง), โดเมน {0,1,2,3} ถึงช่วง {0,1,2,3} คำตอบแต่ละข้อมีวิธีการใช้แผนที่นั้น

นอกจากนี้คุณยังสามารถดูคำตอบจำนวนหนึ่งการปรับปรุงใหม่ของปัญหาเป็นแผนที่ 1 ฐาน 2 หลัก 4 จำนวน N (หนึ่งสอง) โดยที่หนึ่งคือหลัก 1 สองคือหลัก 2 และ N = 4 * หนึ่ง + สอง; N = {0,1,2, ... , 15} - ค่าต่างกันสิบหกค่านั่นสำคัญ เอาต์พุตของฟังก์ชันคือหนึ่งฐาน 1 หลัก 4 หมายเลข {0,1,2,3} - ค่าที่แตกต่างกัน 4 ค่าก็มีความสำคัญเช่นกัน

ตอนนี้ตัวเลขฐาน 1 หลัก 4 สามารถแสดงเป็นเลขฐาน 2 หลักได้ {0,1,2,3} = {00,01,10,11} ดังนั้นแต่ละเอาต์พุตสามารถเข้ารหัสด้วย 2 บิตเท่านั้น จากด้านบนมีเอาต์พุตที่แตกต่างกันเพียง 16 ตัวเท่านั้นดังนั้น 16 * 2 = 32 บิตเป็นสิ่งที่จำเป็นสำหรับการเข้ารหัสแผนที่ทั้งหมด ทั้งหมดนี้สามารถพอดีกับ 1 จำนวนเต็ม

ค่าคงที่ M คือการเข้ารหัสของแผนที่ m โดยที่ m (0) ถูกเข้ารหัสเป็นบิต M [0: 1], m (1) ถูกเข้ารหัสเป็นบิต M [2: 3] และ m (n) ถูกเข้ารหัสเป็นบิต M [n * 2: n * 2 + 1]

สิ่งที่เหลืออยู่คือการจัดทำดัชนีและส่งคืนค่าคงที่ส่วนในกรณีนี้คุณสามารถเลื่อน M ขวา 2 * N ครั้งและรับบิตที่มีนัยสำคัญน้อย 2 นั่นคือ (M >> 2 * N) & 0x3 การแสดงออก (หนึ่ง << 3) และ (สอง << 1) เป็นเพียงการคูณสิ่งต่าง ๆ ในขณะที่สังเกตว่า 2 * x = x << 1 และ 8 * x = x << 3


79
ฉลาด แต่ไม่มีใครอ่านรหัสจะมีโอกาสเข้าใจมัน
Aran Mulholland

106
ฉันคิดว่านี่เป็นวิธีที่แย่มาก ไม่มีใครนอกจากผู้เขียนจะเข้าใจว่า คุณต้องการดูโค้ดและเข้าใจมันอย่างรวดเร็ว แต่นี่เป็นเพียงการเสียเวลา
BalázsNémeth

14
ฉันอยู่กับ @ BalázsMáriaNémethเกี่ยวกับเรื่องนี้ แม้ว่าจะเป็นเรื่องที่น่าประทับใจมาก แต่คุณควรเขียนโค้ดสำหรับโรคจิตที่รุนแรง!
OrhanC1

90
downvoters ทุกคนคิดว่านี่เป็นกลิ่นรหัสน่าเกลียด ผู้มีส่วนร่วมทุกคนคิดแบบเดียวกัน แต่ชื่นชมความฉลาดที่อยู่เบื้องหลัง +1 (อย่าใช้รหัสนี้เลย)
usr

4
เป็นตัวอย่างที่ดีของการเขียนโค้ดเท่านั้น !
lealand

98

ฉันไม่ชอบวิธีแก้ไขปัญหาใด ๆ ที่นำเสนอยกเว้น JAB's ไม่มีคนอื่น ๆ ทำให้ง่ายต่อการอ่านรหัสและเข้าใจสิ่งที่จะถูกคำนวณ

นี่คือวิธีที่ฉันจะเขียนรหัสนี้ - ฉันรู้เพียง C # ไม่ใช่ Java แต่คุณได้รับรูปภาพ:

const bool t = true;
const bool f = false;
static readonly bool[,] attackResult = {
    { f, f, t, f }, 
    { f, f, f, t },
    { f, t, t, t },
    { t, f, t, t }
};
[Flags] enum HitResult 
{ 
    Neither = 0,
    PlayerOne = 1,
    PlayerTwo = 2,
    Both = PlayerOne | PlayerTwo
}
static HitResult ResolveAttack(int one, int two)
{
    return 
        (attackResult[one, two] ? HitResult.PlayerOne : HitResult.Neither) | 
        (attackResult[two, one] ? HitResult.PlayerTwo : HitResult.Neither);
}    

ตอนนี้มันชัดเจนมากขึ้นว่ามีการคำนวณอะไรอยู่ที่นี่: นี่เป็นการเน้นว่าเรากำลังประมวลผลผู้ที่ถูกโจมตีด้วยการโจมตีแบบใดและส่งคืนผลลัพธ์ทั้งสองอย่าง

อย่างไรก็ตามสิ่งนี้อาจดีกว่านี้ อาร์เรย์ Boolean นั้นค่อนข้างทึบแสง ฉันชอบวิธีการค้นหาแบบตาราง แต่ฉันอยากจะเขียนในลักษณะที่ทำให้ชัดเจนว่าความหมายของเกมที่ต้องการคืออะไร นั่นคือแทนที่จะเป็น "การโจมตีศูนย์และการป้องกันผลเดียวโดยไม่มีผลกระทบ" แทนที่จะหาวิธีที่จะทำให้รหัสชัดเจนยิ่งขึ้นว่า "การโจมตีแบบเตะต่ำและการป้องกันแบบบล็อกระดับต่ำในแบบที่ไม่มีการโจมตี" ทำให้รหัสสะท้อนถึงตรรกะทางธุรกิจของเกม


66
เรื่องไร้สาระ โปรแกรมที่มีประสบการณ์อย่างอ่อนโยนส่วนใหญ่จะสามารถชื่นชมคำแนะนำที่ได้รับจากที่นี่และใช้รูปแบบการเข้ารหัสกับตัวเลือกของพวกเขาเอง คำถามคือทำอย่างไรจึงจะหลีกเลี่ยงสตริงของ ifs สิ่งนี้แสดงให้เห็นว่า
GreenAsJade

6
@ user3414693: ฉันรู้ดีว่าเป็นคำถามของจาวา หากคุณอ่านคำตอบอย่างระมัดระวังที่ชัดเจน หากคุณเชื่อว่าคำตอบของฉันไม่ฉลาดฉันขอแนะนำให้คุณเขียนคำตอบของคุณเองว่าคุณชอบมากขึ้น
Eric Lippert

1
@EricLippert ฉันชอบโซลูชันของ JAB ด้วย IMHO ประเภท enum ใน C # จะเป็นที่ต้องการอย่างมาก มันไม่ได้เป็นไปตามปรัชญาแห่งความสำเร็จที่คุณสมบัติอื่น ๆ ทำ เช่นstackoverflow.com/a/847353/92414 c # ทีมมีแผนใด ๆ ที่จะสร้างประเภท enum ใหม่ (เพื่อหลีกเลี่ยงการทำลายรหัสที่มีอยู่) ซึ่งออกแบบมาดีกว่าหรือไม่
SolutionYogi

@SolutionYogi: ฉันไม่ชอบ enums ใน C # เช่นกันถึงแม้ว่าพวกเขาจะเป็นวิธีที่พวกเขามีเหตุผลทางประวัติศาสตร์ที่ดี (ส่วนใหญ่เข้ากันได้กับ COM enums ที่มีอยู่) ฉันไม่ทราบแผนการที่จะเพิ่มอุปกรณ์ใหม่สำหรับ enums ใน C # 6
Eric Lippert

3
@ ไม่มีรายการความคิดเห็นไม่ทำงาน OP ทำในสิ่งที่ควรทำ แปลงความคิดเห็นเป็นรหัสที่ชัดเจน ดูตัวอย่างเช่น Steve McConnell Code Complete stevemcconnell.com/cccntnt.htm
djechlin

87

คุณสามารถสร้างเมทริกซ์ซึ่งมีผลลัพธ์

int[][] results = {{0, 0, 1, 2}, {0, 0, 2, 1},{2, 1, 3, 3},{2, 1, 3, 3}};

เมื่อคุณต้องการได้รับความคุ้มค่าคุณจะใช้

public int fightMath(int one, int two) {
  return this.results[one][two]; 
}

69

คนอื่นได้แนะนำแนวคิดเริ่มต้นของฉันแล้ววิธีเมทริกซ์ แต่นอกเหนือจากการรวมคำสั่ง if คุณสามารถหลีกเลี่ยงบางสิ่งที่คุณมีโดยทำให้แน่ใจว่าข้อโต้แย้งที่ให้นั้นอยู่ในช่วงที่คาดหวังและโดยใช้การส่งคืนแบบแทน มาตรฐานที่ฉันเคยเห็นบังคับให้ใช้จุดเดียวออกจากฟังก์ชั่น แต่ฉันพบว่าผลตอบแทนหลายรายการมีประโยชน์มากสำหรับการหลีกเลี่ยงการเข้ารหัสลูกศรและด้วยความชุกของข้อยกเว้นใน Java ไม่มีจุดใดที่บังคับใช้กฎดังกล่าวอย่างเคร่งครัดอยู่แล้ว เนื่องจากมีข้อยกเว้นที่ไม่ถูกตรวจจับใด ๆ ที่ถูกโยนเข้าไปในวิธีการนั้นเป็นจุดออกที่เป็นไปได้) คำสั่งสวิตช์ซ้อนเป็นไปได้ แต่สำหรับช่วงเล็ก ๆ ของค่าที่คุณกำลังตรวจสอบที่นี่ฉันพบว่าคำสั่งมีขนาดกะทัดรัดและไม่น่าจะส่งผลให้เกิดความแตกต่างด้านประสิทธิภาพมากนัก

public int fightMath(int one, int two) {
    if (one > 3 || one < 0 || two > 3 || two < 0) {
        throw new IllegalArgumentException("Result is undefined for arguments outside the range [0, 3]");
    }

    if (one <= 1) {
        if (two <= 1) return 0;
        if (two - one == 2) return 1;
        return 2; // two can only be 3 here, no need for an explicit conditional
    }

    // one >= 2
    if (two >= 2) return 3;
    if (two == 1) return 1;
    return 2; // two can only be 0 here
}

นี่จะจบลงด้วยการอ่านน้อยกว่ามันอาจเป็นเพราะความผิดปกติของส่วนต่าง ๆ ของการแมปอินพุท -> ฉันชอบสไตล์เมทริกซ์แทนเนื่องจากความเรียบง่ายและวิธีที่คุณสามารถตั้งค่าเมทริกซ์เพื่อให้เห็นภาพได้ (แม้ว่าส่วนนั้นจะได้รับอิทธิพลจากความทรงจำของแผนที่ Karnaugh ของฉัน):

int[][] results = {{0, 0, 1, 2},
                   {0, 0, 2, 1},
                   {2, 1, 3, 3},
                   {2, 1, 3, 3}};

อัปเดต: เมื่อคุณพูดถึงการปิดกั้น / การกดปุ่มนี่คือการเปลี่ยนแปลงที่รุนแรงยิ่งขึ้นของฟังก์ชันที่ใช้ประเภทที่ระบุ / การถือครองแอตทริบิวต์ที่เหมาะสมสำหรับการป้อนข้อมูลและผลลัพธ์และยังแก้ไขผลลัพธ์เล็กน้อยสำหรับการบล็อกซึ่งจะทำให้ ฟังก์ชั่นที่อ่านได้

enum MoveType {
    ATTACK,
    BLOCK;
}

enum MoveHeight {
    HIGH,
    LOW;
}

enum Move {
    // Enum members can have properties/attributes/data members of their own
    ATTACK_HIGH(MoveType.ATTACK, MoveHeight.HIGH),
    ATTACK_LOW(MoveType.ATTACK, MoveHeight.LOW),
    BLOCK_HIGH(MoveType.BLOCK, MoveHeight.HIGH),
    BLOCK_LOW(MoveType.BLOCK, MoveHeight.LOW);

    public final MoveType type;
    public final MoveHeight height;

    private Move(MoveType type, MoveHeight height) {
        this.type = type;
        this.height = height;
    }

    /** Makes the attack checks later on simpler. */
    public boolean isAttack() {
        return this.type == MoveType.ATTACK;
    }
}

enum LandedHit {
    NEITHER,
    PLAYER_ONE,
    PLAYER_TWO,
    BOTH;
}

LandedHit fightMath(Move one, Move two) {
    // One is an attack, the other is a block
    if (one.type != two.type) {
        // attack at some height gets blocked by block at same height
        if (one.height == two.height) return LandedHit.NEITHER;

        // Either player 1 attacked or player 2 attacked; whoever did
        // lands a hit
        if (one.isAttack()) return LandedHit.PLAYER_ONE;
        return LandedHit.PLAYER_TWO;
    }

    // both attack
    if (one.isAttack()) return LandedHit.BOTH;

    // both block
    return LandedHit.NEITHER;
}

คุณไม่จำเป็นต้องเปลี่ยนฟังก์ชั่นเองหากคุณต้องการเพิ่มบล็อก / การโจมตีที่มีความสูงมากขึ้น การเพิ่มประเภทของการย้ายเพิ่มเติมอาจจะต้องมีการปรับเปลี่ยนฟังก์ชั่น นอกจากนี้EnumSetsอาจขยายได้มากกว่าการใช้ enums พิเศษเป็นคุณสมบัติของ enum หลักเช่นEnumSet<Move> attacks = EnumSet.of(Move.ATTACK_HIGH, Move.ATTACK_LOW, ...);จากนั้นattacks.contains(move)แทนที่จะmove.type == MoveType.ATTACKใช้แม้ว่าการใช้EnumSets จะช้ากว่าการตรวจสอบเท่ากับโดยตรงโดยตรงเล็กน้อย


สำหรับกรณีที่บล็อกที่ประสบความสำเร็จส่งผลให้ตัวนับคุณสามารถแทนที่if (one.height == two.height) return LandedHit.NEITHER;ด้วย

if (one.height == two.height) {
    // Successful block results in a counter against the attacker
    if (one.isAttack()) return LandedHit.PLAYER_TWO;
    return LandedHit.PLAYER_ONE;
}

นอกจากนี้การแทนที่ifคำสั่งบางส่วนด้วยการใช้ตัวดำเนินการแบบไตรภาค ( boolean_expression ? result_if_true : result_if_false) อาจทำให้โค้ดมีขนาดกะทัดรัดมากขึ้น (ตัวอย่างเช่นโค้ดในบล็อกก่อนหน้าจะกลายเป็นreturn one.isAttack() ? LandedHit.PLAYER_TWO : LandedHit.PLAYER_ONE;) แต่นั่นอาจทำให้ผู้อ่านยากขึ้น ไม่แนะนำสำหรับการแยกที่ซับซ้อนมากขึ้น


ฉันจะดูอย่างนี้แน่นอน แต่รหัสปัจจุบันของฉันอนุญาตให้ฉันใช้ค่า int oneและtwoนำกลับมาใช้ใหม่เป็นจุดเริ่มต้นบน Spritesheet ของฉัน แม้ว่าจะไม่จำเป็นต้องใช้รหัสเพิ่มเติมอีกมากในการอนุญาต
TomFirth

2
@ TomFirth84 มีเป็นEnumMapระดับที่คุณสามารถใช้เพื่อแม enums เพื่อชดเชยจำนวนเต็ม (คุณยังสามารถใช้ค่าลำดับสมาชิก enum โดยตรงตัวอย่างเช่นMove.ATTACK_HIGH.ordinal()จะ0, Move.ATTACK_LOW.ordinal()จะเป็น1ฯลฯ แต่ที่เปราะบางมากขึ้น / มีความยืดหยุ่นน้อยกว่าอย่างชัดเจน การเชื่อมโยงสมาชิกแต่ละคนด้วยค่าเป็นการเพิ่มค่า enum ในระหว่างสมาชิกที่มีอยู่จะสลัดการนับซึ่งจะไม่ใช่กรณีที่มีEnumMap)
JAB

7
นี่เป็นวิธีการแก้ปัญหาที่สามารถอ่านได้มากที่สุดเพราะแปลรหัสเป็นสิ่งที่มีความหมายสำหรับผู้ที่อ่านรหัส
David Stanley

รหัสของคุณอย่างน้อยหนึ่งรหัสที่ใช้ enums นั้นผิด ตามคำสั่ง if ใน OP บล็อกที่ประสบความสำเร็จจะนำไปสู่การโจมตีของผู้โจมตี แต่ +1 สำหรับรหัสเต็มความหมาย
Taemyr

2
คุณสามารถเพิ่มattack(against)วิธีการในMoveenum, คืนค่า HIT เมื่อการโจมตีสำเร็จ, BACKFIRE เมื่อการเคลื่อนไหวเป็นการโจมตีที่ถูกบล็อกและไม่มีอะไรเกิดขึ้นเมื่อไม่มีการโจมตี วิธีนี้คุณสามารถนำไปใช้โดยทั่วไป ( public boolean attack(Move other) { if this.isAttack() return (other.isAttack() || other.height != this.height) ? HIT : BACKFIRE; return NOTHING; }) และแทนที่มันในการเคลื่อนไหวที่เฉพาะเจาะจงเมื่อจำเป็น (การเคลื่อนไหวที่อ่อนแอซึ่งบล็อกใด ๆ สามารถบล็อกการโจมตีที่ไม่เคยย้อนกลับเป็นต้น)
เขียนใหม่

50

ทำไมไม่ใช้อาร์เรย์?

ฉันจะเริ่มจากจุดเริ่มต้น ฉันเห็นรูปแบบค่าไปจาก 0 ถึง 3 และคุณต้องการจับค่าที่เป็นไปได้ทั้งหมด นี่คือตารางของคุณ:

0 & 0 = 0
0 & 1 = 0
0 & 2 = 1
0 & 3 = 2
1 & 0 = 0
1 & 1 = 0
1 & 2 = 2
1 & 3 = 1
2 & 0 = 2
2 & 1 = 1
2 & 2 = 3
2 & 3 = 3
3 & 0 = 2
3 & 1 = 1
3 & 2 = 3
3 & 3 = 3

เมื่อเราดูที่ไบนารีตารางเดียวกันนี้เราจะเห็นผลลัพธ์ต่อไปนี้:

00 & 00 = 00
00 & 01 = 00
00 & 10 = 01
00 & 11 = 10
01 & 00 = 00
01 & 01 = 00
01 & 10 = 10
01 & 11 = 01
10 & 00 = 10
10 & 01 = 01
10 & 10 = 11
10 & 11 = 11
11 & 00 = 10
11 & 01 = 01
11 & 10 = 11
11 & 11 = 11

ตอนนี้บางทีคุณอาจเห็นรูปแบบบางอย่างแล้ว แต่เมื่อฉันรวมค่าหนึ่งกับสองฉันเห็นว่าคุณกำลังใช้ค่าทั้งหมด 0000, 0001, 0010, ..... 1110 และ 1111 ตอนนี้ลองรวมค่าหนึ่งและสองเข้าด้วยกัน จำนวนเต็ม 4 บิต

0000 = 00
0001 = 00
0010 = 01
0011 = 10
0100 = 00
0101 = 00
0110 = 10
0111 = 01
1000 = 10
1001 = 01
1010 = 11
1011 = 11
1100 = 10
1101 = 01
1110 = 11
1111 = 11

เมื่อเราแปลกลับไปเป็นค่าทศนิยมเราจะเห็นอาร์เรย์ของค่าที่เป็นไปได้มากโดยที่การรวมกันหนึ่งและสองสามารถใช้เป็นดัชนี:

0 = 0
1 = 0
2 = 1
3 = 2
4 = 0
5 = 0
6 = 2
7 = 1
8 = 2
9 = 1
10 = 3
11 = 3
12 = 2
13 = 1
14 = 3
15 = 3

อาร์เรย์คือ{0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3}ที่ซึ่งดัชนีเป็นเพียงหนึ่งและสองรวมกัน

ฉันไม่ใช่โปรแกรมเมอร์ Java แต่คุณสามารถกำจัดได้ถ้าข้อความสั่งและเขียนมันลงไปอย่างนี้:

int[] myIntArray = {0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3};
result = myIntArray[one * 4 + two]; 

ฉันไม่รู้ว่า bitshift 2 เป็นเร็วกว่าการคูณ แต่มันก็คุ้มค่าที่จะลอง


2
บิตกะ 2 นั้นเกือบจะเร็วกว่าการคูณ 4 แน่นอนดีที่สุดการคูณด้วย 4 จะรับรู้ว่า 4 คือ 2 ^ 2 และทำการกะบิตเอง (แปลโดยคอมไพเลอร์อาจ) สำหรับฉันแล้วการเปลี่ยนแปลงนั้นสามารถอ่านได้ง่ายขึ้น
Cruncher

ฉันชอบแนวทางของคุณ! มันจะปรับเมทริกซ์ 4x4 ให้แบนลงในอาร์เรย์องค์ประกอบ 16 ตัว
Cameron Tinker

6
ทุกวันนี้ถ้าฉันไม่เข้าใจผิดคอมไพเลอร์จะรับรู้อย่างไม่ต้องสงสัยว่าคุณกำลังทวีคูณด้วยพลังสองเท่าและปรับให้เหมาะสมตามนั้น ดังนั้นสำหรับคุณโปรแกรมเมอร์โปรแกรมเมอร์การเปลี่ยนแปลงบิตและการคูณควรมีประสิทธิภาพเหมือนกันทุกประการ
แทนเนอร์ Swett

24

วิธีนี้ใช้ bitmagic นิดหน่อย (คุณได้ทำไปแล้วโดยการเก็บข้อมูลสองบิต (ต่ำ / สูง & โจมตี / บล็อก) ในจำนวนเต็มเดียว):

ฉันยังไม่ได้เรียกใช้พิมพ์เฉพาะที่นี่โปรดตรวจสอบอีกครั้ง ความคิดที่แน่นอนใช้งานได้ แก้ไข:ตอนนี้ได้รับการทดสอบสำหรับทุกอินพุตทำงานได้ดี

public int fightMath(int one, int two) {
    if(one<2 && two<2){ //both players blocking
        return 0; // nobody hits
    }else if(one>1 && two>1){ //both players attacking
        return 3; // both hit
    }else{ // some of them attack, other one blocks
        int different_height = (one ^ two) & 1; // is 0 if they are both going for the same height - i.e. blocker wins, and 1 if height is different, thus attacker wins
        int attacker = one>1?1:0; // is 1 if one is the attacker, two is the blocker, and 0 if one is the blocker, two is the attacker
        return (attacker ^ different_height) + 1;
    }
}

หรือฉันควรแนะนำให้แยกข้อมูลสองบิตออกเป็นตัวแปรแยกกันหรือไม่ รหัสตามการดำเนินการบิตส่วนใหญ่เช่นนี้มักจะยากที่จะรักษา


2
ฉันเห็นด้วยกับวิธีนี้ดูเหมือนว่าฉันมีความคิดเห็นในคำถามหลัก ฉันต้องการแยกมันออกเป็นตัวแปรแยกซึ่งจะทำให้ง่ายขึ้นในการเพิ่มการโจมตีระดับกลางในอนาคต
Yoh

2
ฉันเพิ่งแก้ไขข้อบกพร่องบางอย่างในรหัสด้านบนหลังจากการทดสอบตอนนี้มันทำงานได้ดี จะไปในทางที่บิตหุ่นยนต์ของฉันยังมาพร้อมกับวิธีการแก้ปัญหาหนึ่งบรรทัดซึ่งยังคงไม่เป็นที่ลึกลับเป็น bitmask ในคำตอบอื่น ๆ แต่ยังคงพอหากินเพื่อกรูใจของคุณ:return ((one ^ two) & 2) == 0 ? (one & 2) / 2 * 3 : ((one & 2) / 2 ^ ((one ^ two) & 1)) + 1;
อีเลียส

1
นี่เป็นคำตอบที่ดีที่สุดเนื่องจากโปรแกรมเมอร์คนใหม่ที่กำลังอ่านอยู่นั้นจะเข้าใจถึงความมหัศจรรย์ที่เกิดขึ้นเบื้องหลังตัวเลขเวทย์มนตร์เหล่านั้นทั้งหมด
bezmax

20

พูดตามตรงแล้วทุกคนก็มีโค้ดของตัวเอง ฉันไม่คิดว่าการแสดงจะได้รับผลกระทบมากเกินไป หากคุณเข้าใจสิ่งนี้ดีกว่าการใช้เวอร์ชั่นตัวเรือนสวิตช์ให้ใช้งานตัวนี้ต่อไป

คุณสามารถซ้อน ifs ได้ดังนั้นอาจมีการเพิ่มประสิทธิภาพเล็กน้อยสำหรับการตรวจสอบครั้งล่าสุดของคุณหากการตรวจสอบตามที่ไม่ได้ผ่านมากถ้างบ แต่ในบริบทของหลักสูตรภาษาจาวาพื้นฐานของคุณมันอาจจะไม่ได้รับประโยชน์

else if(one == 3 && two == 3) { result = 3; }

ดังนั้นแทนที่จะ ...

if(one == 0 && two == 0) { result = 0; }
else if(one == 0 && two == 1) { result = 0; }
else if(one == 0 && two == 2) { result = 1; }
else if(one == 0 && two == 3) { result = 2; }

คุณจะทำ ...

if(one == 0) 
{ 
    if(two == 0) { result = 0; }
    else if(two == 1) { result = 0; }
    else if(two == 2) { result = 1; }
    else if(two == 3) { result = 2; }
}

และเพียงฟอร์แมตใหม่ตามที่คุณต้องการ

นี่ไม่ได้ทำให้โค้ดดูดีขึ้น แต่อาจเพิ่มความเร็วขึ้นเล็กน้อยที่ฉันเชื่อ


3
ไม่ทราบว่าเป็นวิธีปฏิบัติที่ดีจริง ๆ หรือไม่ แต่สำหรับกรณีนี้ฉันอาจใช้คำสั่ง switch แบบซ้อน มันจะใช้พื้นที่มากขึ้น แต่มันจะชัดเจนจริงๆ
สีย้อม

มันอาจใช้ได้ แต่ฉันคิดว่ามันเป็นเรื่องของความชอบ ฉันชอบถ้างบเพราะมันบอกว่ารหัสกำลังทำอะไรอยู่ ไม่เอาความคิดของคุณลงไปอะไรก็ได้ที่เหมาะกับคุณ :) โหวตขึ้นสำหรับคำแนะนำทางเลือกแม้ว่า!
Joe Harper

12

เรามาดูกันว่าเรารู้อะไรบ้าง

1: คำตอบของคุณมีความสมมาตรสำหรับ P1 (ผู้เล่นหนึ่งคน) และ P2 (ผู้เล่นสองคน) มันสมเหตุสมผลสำหรับเกมต่อสู้ แต่ก็เป็นสิ่งที่คุณสามารถใช้เพื่อพัฒนาตรรกะของคุณ

2: 3 ครั้ง 0 ครั้ง 2 ครั้ง 1 ครั้ง 3. กรณีเดียวที่ไม่ครอบคลุมในกรณีนี้คือการรวมกันของ 0 กับ 1 และ 2 กับ 3 เมื่อต้องการใส่อีกวิธีหนึ่งตารางชัยชนะที่ไม่ซ้ำกันมีลักษณะเช่นนี้: 0 เต้น 2, 1 ครั้ง 3, 2 ครั้งที่ 1, 3 ครั้งที่ 0

3: ถ้า 0/1 ขึ้นไปต่อกันจะมีการเสมอกันระหว่าง hitless แต่ถ้า 2/3 ขึ้นไปต่อกันจะมีการตีทั้งคู่

ก่อนอื่นให้เราสร้างฟังก์ชั่นทางเดียวแจ้งให้เราทราบหากเราชนะ:

// returns whether we beat our opponent
public boolean doesBeat(int attacker, int defender) {
  int[] beats = {2, 3, 1, 0};
  return defender == beats[attacker];
}

จากนั้นเราสามารถใช้ฟังก์ชันนี้เพื่อเขียนผลลัพธ์สุดท้าย:

// returns the overall fight result
// bit 0 = one hits
// bit 1 = two hits
public int fightMath(int one, int two)
{
  // Check to see whether either has an outright winning combo
  if (doesBeat(one, two))
    return 1;

  if (doesBeat(two, one))
    return 2;

  // If both have 0/1 then its hitless draw but if both have 2/3 then they both hit.
  // We can check this by seeing whether the second bit is set and we need only check
  // one's value as combinations where they don't both have 0/1 or 2/3 have already
  // been dealt with 
  return (one & 2) ? 3 : 0;
}

แม้ว่านี่จะซับซ้อนกว่าและอาจช้ากว่าการค้นหาตารางที่มีคำตอบมากมาย แต่ฉันเชื่อว่ามันเป็นวิธีการที่เหนือกว่าเพราะจริง ๆ แล้วมันห่อหุ้มตรรกะของรหัสของคุณและอธิบายให้คนที่อ่านโค้ดของคุณ ฉันคิดว่าสิ่งนี้ทำให้การใช้งานที่ดีขึ้น

(ไม่นานมานี้เนื่องจากฉันทำ Java ใด ๆ จึงขอโทษถ้าปิดไวยากรณ์หวังว่ามันคงเข้าใจได้ถ้าฉันทำผิดเล็กน้อย)

โดยวิธีการที่ 0-3 อย่างชัดเจนหมายถึงบางสิ่งบางอย่าง; พวกมันไม่ใช่ค่านิยมตามอำเภอใจดังนั้นมันจะช่วยให้ตั้งชื่อพวกมันได้


11

ฉันหวังว่าฉันเข้าใจตรรกะอย่างถูกต้อง เกี่ยวกับสิ่งที่ชอบ:

public int fightMath (int one, int two)
{
    int oneHit = ((one == 3 && two != 1) || (one == 2 && two != 0)) ? 1 : 0;
    int twoHit = ((two == 3 && one != 1) || (two == 2 && one != 0)) ? 2 : 0;

    return oneHit+twoHit;
}

การตรวจสอบหนึ่งตีสูงหรือหนึ่งตีต่ำไม่ได้ถูกบล็อกและเหมือนกันสำหรับผู้เล่นสองคน

แก้ไข: อัลกอริทึมไม่เข้าใจอย่างเต็มที่ "hit" มอบให้เมื่อบล็อกซึ่งฉันไม่ได้ตระหนักถึง (elx ขอบคุณ):

public int fightMath (int one, int two)
{
    int oneAttack = ((one == 3 && two != 1) || (one == 2 && two != 0)) ? 1 : (one >= 2) ? 2 : 0;
    int twoAttack = ((two == 3 && one != 1) || (two == 2 && one != 0)) ? 2 : (two >= 2) ? 1 : 0;

    return oneAttack | twoAttack;
}

วิธีการค้นหาผลลัพธ์ที่ยอดเยี่ยมลาย!
ชาด

1
ฉันชอบวิธีการนี้ แต่ฉันกลัวว่าวิธีนี้จะไม่มีความสามารถในการกดปุ่มโดยการบล็อกการโจมตี (เช่นถ้าหนึ่ง = 0 และสอง = 2 จะได้ผลลัพธ์เป็น 0 แต่คาดว่า 1 ตามข้อกำหนด) บางทีคุณสามารถทำงานกับมันเพื่อให้ถูกต้อง แต่ฉันไม่แน่ใจว่ารหัสผลลัพธ์จะยังคงสง่างามเช่นนี้หมายความว่าเส้นจะเติบโตค่อนข้างยาว
elias

ไม่ทราบว่าได้รับ "hit" สำหรับบล็อก ขอบคุณที่ชี้นำ ปรับด้วยการแก้ไขที่ง่ายมาก
Chris

10

ฉันไม่ได้มีประสบการณ์กับ Java ดังนั้นอาจมีการพิมพ์ผิด โปรดพิจารณารหัสเป็นรหัสหลอก

ฉันจะไปกับสวิตช์ง่าย ๆ เพื่อที่คุณจะต้องประเมินหมายเลขเดียว แต่สำหรับกรณีนี้ตั้งแต่ปี0 <= one < 4 <= 9และ0 <= two < 4 <= 9เราสามารถแปลง ints ทั้งกับ int ง่ายโดยการคูณoneด้วย 10 twoและการเพิ่ม จากนั้นใช้สวิตช์ในหมายเลขผลลัพธ์ดังนี้:

public int fightMath(int one, int two) {
    // Convert one and two to a single variable in base 10
    int evaluate = one * 10 + two;

    switch(evaluate) {
        // I'd consider a comment in each line here and in the original code
        // for clarity
        case 0: result = 0; break;
        case 1: result = 0; break;
        case 1: result = 0; break;
        case 2: result = 1; break;
        case 3: result = 2; break;
        case 10: result = 0; break;
        case 11: result = 0; break;
        case 12: result = 2; break;
        case 13: result = 1; break;
        case 20: result = 2; break;
        case 21: result = 1; break;
        case 22: result = 3; break;
        case 23: result = 3; break;
        case 30: result = 1; break;
        case 31: result = 2; break;
        case 32: result = 3; break;
        case 33: result = 3; break;
    }

    return result;
}

มีอีกวิธีสั้น ๆ ที่ฉันต้องการชี้ให้เห็นว่าเป็นรหัสทฤษฎี อย่างไรก็ตามฉันจะไม่ใช้มันเพราะมันมีความซับซ้อนเป็นพิเศษที่คุณไม่ต้องการจัดการ ความซับซ้อนพิเศษมาจากฐาน 4เนื่องจากการนับเป็น 0, 1, 2, 3, 10, 11, 12, 13, 20, ...

public int fightMath(int one, int two) {
    // Convert one and two to a single variable in base 4
    int evaluate = one * 4 + two;

    allresults = new int[] { 0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 1, 2, 3, 3 };

    return allresults[evaluate];
}

จริงๆแล้วเป็นเพียงบันทึกเพิ่มเติมในกรณีที่ฉันขาดอะไรจาก Java ใน PHP ฉันต้องการ:

function fightMath($one, $two) {
    // Convert one and two to a single variable in base 4
    $evaluate = $one * 10 + $two;

    $allresults = array(
         0 => 0,  1 => 0,  2 => 1,  3 => 2,
        10 => 0, 11 => 0, 12 => 2, 13 => 1,
        20 => 2, 21 => 1, 22 => 3, 23 => 3,
        30 => 1, 31 => 2, 32 => 3, 33 => 3 );

    return $allresults[$evaluate];
}

Java ไม่มี lamdas ก่อนรุ่นที่ 8
Kirill Gamazkov

1
นี้. สำหรับอินพุตจำนวนเล็กน้อยเช่นนี้ฉันจะใช้สวิตช์ที่มีค่าคอมโพสิต (แม้ว่ามันจะสามารถอ่านได้มากกว่าด้วยตัวคูณที่มากกว่า 10 เช่น 100 หรือ 1,000)
Medinoc

7

เนื่องจากคุณชอบifเงื่อนไขที่ซ้อนกันนี่เป็นอีกวิธีหนึ่ง
โปรดทราบว่ามันไม่ได้ใช้resultสมาชิกและจะไม่เปลี่ยนสถานะใด ๆ

public int fightMath(int one, int two) {
    if (one == 0) {
      if (two == 0) { return 0; }
      if (two == 1) { return 0; }
      if (two == 2) { return 1; }
      if (two == 3) { return 2; }
    }   
    if (one == 1) {
      if (two == 0) { return 0; }
      if (two == 1) { return 0; }
      if (two == 2) { return 2; }
      if (two == 3) { return 1; }
    }
    if (one == 2) {
      if (two == 0) { return 2; }
      if (two == 1) { return 1; }
      if (two == 2) { return 3; }
      if (two == 3) { return 3; }
    }
    if (one == 3) {
      if (two == 0) { return 1; }
      if (two == 1) { return 2; }
      if (two == 2) { return 3; }
      if (two == 3) { return 3; }
    }
    return DEFAULT_RESULT;
}

ทำไมคุณไม่มีอะไรอีก
FDinoff

3
@Finin ฉันสามารถใช้elseโซ่ แต่มันก็ไม่ได้สร้างความแตกต่าง
Nick Dandoulakis

1
ฉันรู้ว่ามันไม่สำคัญ แต่จะไม่เพิ่มเชนอื่น ๆ ที่ทำงานได้เร็วขึ้นใช่ไหม ใน 3 จาก 4 กรณี? ฉันมักจะเขียนรหัสเพื่อให้ทำงานได้อย่างรวดเร็วที่สุดแม้ว่าจะเป็นเพียงไม่กี่รอบก็ตาม
Brandon Bearden

2
@BrandonBearden ที่นี่พวกเขาจะไม่สร้างความแตกต่าง (สมมติว่าอินพุตอยู่ในช่วง 0..3 เสมอ) คอมไพเลอร์อาจจะไมโครโค้ดเพิ่มประสิทธิภาพอยู่ดี หากเรามีชุดelse ifข้อความยาวเราสามารถเพิ่มโค้ดให้เร็วขึ้นswitchหรือใช้ตารางค้นหา
Nick Dandoulakis

เป็นเช่นนั้นได้อย่างไร ถ้าone==0มันจะเรียกใช้รหัสก็จะต้องตรวจสอบว่าถ้าone==1แล้วone==2และในที่สุดถ้าone==3- ถ้ามีในนั้นมันจะไม่ทำการตรวจสอบสามครั้งล่าสุดเพราะมันจะออกจากคำสั่งหลังจากการแข่งขันครั้งแรก และใช่คุณสามารถเพิ่มประสิทธิภาพโดยใช้งบเปลี่ยนในสถานที่ของงบแล้วต่อโดยใช้สวิทช์อีกในกรณีของif (one... one'sอย่างไรก็ตามนั่นไม่ใช่คำถามของฉัน
Brandon Bearden

6

ลองใช้ด้วยปลอกสวิตช์..

ลองดูที่นี่หรือที่นี่สำหรับข้อมูลเพิ่มเติมเกี่ยวกับมัน

switch (expression)
{ 
  case constant:
        statements;
        break;
  [ case constant-2:
        statements;
        break;  ] ...
  [ default:
        statements;
        break;  ] ...
}

คุณสามารถเพิ่มหลายเงื่อนไข (ไม่พร้อมกัน) และยังมีตัวเลือกเริ่มต้นที่ไม่มีกรณีอื่น ๆ ได้รับความพึงพอใจ

ป.ล. : เฉพาะในกรณีที่เงื่อนไขเป็นที่พึงพอใจ ..

หาก 2 เงื่อนไขเกิดขึ้นพร้อมกัน .. ฉันไม่คิดว่าสามารถใช้สวิตช์ได้ แต่คุณสามารถลดรหัสได้ที่นี่

Java switch คำสั่งหลายกรณี


6

สิ่งแรกที่เกิดขึ้นกับฉันคือคำตอบเดียวกันกับที่ Francisco Presencia ได้รับ แต่ได้รับการปรับให้เหมาะสม:

public int fightMath(int one, int two)
{
    switch (one*10 + two)
    {
    case  0:
    case  1:
    case 10:
    case 11:
        return 0;
    case  2:
    case 13:
    case 21:
    case 30:
        return 1;
    case  3:
    case 12:
    case 20:
    case 31:
        return 2;
    case 22:
    case 23:
    case 32:
    case 33:
        return 3;
    }
}

คุณสามารถปรับให้เหมาะสมโดยการใช้ตัวพิมพ์สุดท้าย (สำหรับ 3) ตัวพิมพ์ใหญ่:

    //case 22:
    //case 23:
    //case 32:
    //case 33:
    default:
        return 3;

ข้อดีของวิธีนี้คือจะง่ายกว่าที่จะเห็นว่ามีค่าใดบ้างoneและtwoสอดคล้องกับค่าส่งคืนใดบ้างกว่าวิธีที่แนะนำอื่น ๆ


นี่คือการเปลี่ยนแปลงของคำตอบของฉันอีกที่นี่
David R Tribble

6
((two&2)*(1+((one^two)&1))+(one&2)*(2-((one^two)&1)))/2

4
ใช้เวลานานเท่าไรกว่าจะถึงจุดนี้?
mbatchkarov

2
@matchkarov ประมาณ 10 นาทีในการอ่านคำตอบอื่น ๆ จากนั้น 10 นาทีในการเขียนหวัดด้วยดินสอและกระดาษ
Dawood ibn Kareem

7
ฉันคงจะเศร้ามากถ้าต้องรักษาสิ่งนี้ไว้
Meryovi

uhmmm ... มีข้อผิดพลาด: คุณหายไป; --unicorns
อัลเบอร์โต

ฉันเห็นด้วยกับ @Meryovi อุปกรณ์ประกอบฉากเพื่อความกระชับ แต่น่ากลัวเหมือนรหัส APL
Ed Griebel

4

คุณสามารถใช้ตัวเรือนสวิทซ์แทนแบบหลายอันได้if

นอกจากนี้ยังกล่าวถึงว่าเนื่องจากคุณมีสองตัวแปรแล้วคุณจะต้องรวมสองตัวแปรเพื่อใช้ในสวิตช์

ตรวจสอบคำสั่ง Java switch นี้เพื่อจัดการกับตัวแปรสองตัว?


3

ขณะที่ฉันวาดตารางระหว่างหนึ่ง / สองกับผลลัพธ์ฉันเห็นรูปแบบเดียว

if(one<2 && two <2) result=0; return;

ข้างต้นจะลด atleast 3 ถ้างบ ฉันไม่เห็นรูปแบบการตั้งค่าและฉันไม่สามารถรวบรวมได้มากจากรหัสที่ให้ - แต่ถ้าตรรกะดังกล่าวสามารถได้รับมันจะลดจำนวนถ้างบ

หวังว่านี่จะช่วยได้


3

จุดที่ดีคือการกำหนดกฎเป็นข้อความคุณสามารถหาสูตรที่ถูกต้องได้ง่ายขึ้นแล้ว สิ่งนี้สกัดจากการนำเสนออาเรย์ที่ดีของ laalto:

{ 0, 0, 1, 2 },
{ 0, 0, 2, 1 },
{ 2, 1, 3, 3 },
{ 1, 2, 3, 3 }

และที่นี่เราไปพร้อมกับความคิดเห็นทั่วไป แต่คุณควรอธิบายในแง่ของกฎ:

if(one<2) // left half
{
    if(two<2) // upper left half
    {
        result = 0; //neither hits
    }
    else // lower left half
    {
        result = 1+(one+two)%2; //p2 hits if sum is even
    }
}
else // right half
{
    if(two<2) // upper right half
    {
        result = 1+(one+two+1)%2; //p1 hits if sum is even
    }
    else // lower right half
    {
        return 3; //both hit
    }
}

แน่นอนคุณสามารถกระทืบรหัสน้อยกว่านี้ได้ แต่โดยทั่วไปแล้วคุณควรทำความเข้าใจว่าคุณใช้รหัสอะไรมากกว่าการหาโซลูชันขนาดกะทัดรัด

if((one<2)&&(two<2)) result = 0; //top left
else if((one>1)&&(two>1)) result = 3; //bottom right
else result = 1+(one+two+((one>1)?1:0))%2; //no idea what that means

คำอธิบายบางอย่างเกี่ยวกับการโจมตี p1 / p2 ที่ซับซ้อนจะดีมากดูน่าสนใจ!


3

ทางออกที่สั้นและยังอ่านได้:

static public int fightMath(int one, int two)
{
    if (one < 2 && two < 2) return 0;
    if (one > 1 && two > 1) return 3;
    int n = (one + two) % 2;
    return one < two ? 1 + n : 2 - n;
}

หรือสั้นกว่า:

static public int fightMath(int one, int two)
{
    if (one / 2 == two / 2) return (one / 2) * 3;
    return 1 + (one + two + one / 2) % 2;
}

ไม่มีหมายเลข "วิเศษ" ใด ๆ ) หวังว่ามันจะช่วยได้


2
สูตรเช่นนี้จะทำให้ไม่สามารถเปลี่ยน (อัปเดต) ผลลัพธ์ของชุดค่าผสมได้ในภายหลัง วิธีเดียวที่จะปรับปรุงสูตรทั้งหมดได้
SNag

2
@SNag: ฉันเห็นด้วยกับที่ ทางออกที่ยืดหยุ่นที่สุดคือการใช้อาร์เรย์ 2 มิติ แต่ผู้เขียนบทความนี้ต้องการสูตรและสิ่งนี้เป็นสูตรที่ดีที่สุดที่คุณจะได้รับจากไอเอฟเอและคณิตศาสตร์ง่ายๆ
PW


1

โดยส่วนตัวฉันชอบที่จะเรียงซ้อนผู้ประกอบการที่สาม:

int result = condition1
    ? result1
    : condition2
    ? result2
    : condition3
    ? result3
    : resultElse;

แต่ในกรณีของคุณคุณสามารถใช้:

final int[] result = new int[/*16*/] {
    0, 0, 1, 2,
    0, 0, 2, 1,
    2, 1, 3, 3,
    1, 2, 3, 3
};

public int fightMath(int one, int two) {
    return result[one*4 + two];
}

หรือคุณสามารถสังเกตเห็นรูปแบบเป็นบิต:

one   two   result

section 1: higher bits are equals =>
both result bits are equals to that higher bits

00    00    00
00    01    00
01    00    00
01    01    00
10    10    11
10    11    11
11    10    11
11    11    11

section 2: higher bits are different =>
lower result bit is inverse of lower bit of 'two'
higher result bit is lower bit of 'two'

00    10    01
00    11    10
01    10    10
01    11    01
10    00    10
10    01    01
11    00    01
11    01    10

ดังนั้นคุณสามารถใช้เวทย์มนตร์:

int fightMath(int one, int two) {
    int b1 = one & 2, b2 = two & 2;
    if (b1 == b2)
        return b1 | (b1 >> 1);

    b1 = two & 1;

    return (b1 << 1) | (~b1);
}

1

ต่อไปนี้เป็นรุ่นที่รัดกุมค่อนข้างคล้ายกับการตอบสนองของ JAB สิ่งนี้ใช้แผนที่ในการจัดเก็บซึ่งเคลื่อนย้ายชัยชนะเหนือผู้อื่น

public enum Result {
  P1Win, P2Win, BothWin, NeitherWin;
}

public enum Move {
  BLOCK_HIGH, BLOCK_LOW, ATTACK_HIGH, ATTACK_LOW;

  static final Map<Move, List<Move>> beats = new EnumMap<Move, List<Move>>(
      Move.class);

  static {
    beats.put(BLOCK_HIGH, new ArrayList<Move>());
    beats.put(BLOCK_LOW, new ArrayList<Move>());
    beats.put(ATTACK_HIGH, Arrays.asList(ATTACK_LOW, BLOCK_LOW));
    beats.put(ATTACK_LOW, Arrays.asList(ATTACK_HIGH, BLOCK_HIGH));
  }

  public static Result compare(Move p1Move, Move p2Move) {
    boolean p1Wins = beats.get(p1Move).contains(p2Move);
    boolean p2Wins = beats.get(p2Move).contains(p1Move);

    if (p1Wins) {
      return (p2Wins) ? Result.BothWin : Result.P1Win;
    }
    if (p2Wins) {
      return (p1Wins) ? Result.BothWin : Result.P2Win;
    }

    return Result.NeitherWin;
  }
} 

ตัวอย่าง:

System.out.println(Move.compare(Move.ATTACK_HIGH, Move.BLOCK_LOW));

พิมพ์:

P1Win

ฉันอยากจะแนะนำstatic final Map<Move, List<Move>> beats = new java.util.EnumMap<>();แทนมันควรจะมีประสิทธิภาพมากกว่านี้เล็กน้อย
JAB

@ JAB ใช่ความคิดที่ดี ฉันมักจะลืมประเภทนั้นอยู่ และ ... มันช่างน่าประหลาดใจเพียงใดที่จะสร้างมันขึ้นมา!
Duncan Jones

1

ฉันจะใช้แผนที่ไม่ว่าจะเป็น HashMap หรือ TreeMap

โดยเฉพาะอย่างยิ่งถ้าพารามิเตอร์ไม่ได้อยู่ในแบบฟอร์ม 0 <= X < N

เหมือนชุดจำนวนเต็มบวกแบบสุ่ม ..

รหัส

public class MyMap
{
    private TreeMap<String,Integer> map;

    public MyMap ()
    {
        map = new TreeMap<String,Integer> ();
    }

    public void put (int key1, int key2, Integer value)
    {
        String key = (key1+":"+key2);

        map.put(key, new Integer(value));
    }

    public Integer get (int key1, int key2)
    {
        String key = (key1+":"+key2);

        return map.get(key);
    }
}

1

ขอบคุณ @Joe Harper เมื่อฉันลงเอยด้วยการใช้คำตอบที่หลากหลายของเขา เมื่อต้องการลดขนาดให้เล็กลงอีก 2 ผลลัพธ์ต่อ 4 เหมือนกันฉันก็ลดขนาดให้เล็กลงอีก

ฉันอาจกลับมาที่นี่ในบางจุด แต่ถ้าไม่มีการต่อต้านที่สำคัญที่เกิดจากหลายสถานะifแล้วฉันจะเก็บไว้ตอนนี้ ฉันจะดูในตารางเมทริกซ์และเปลี่ยนคำสั่งเพิ่มเติม

public int fightMath(int one, int two) {
  if (one === 0) {
    if (two === 2) { return 1; }
    else if(two === 3) { return 2; }
    else { return 0; }
  } else if (one === 1) {
    if (two === 2) { return 2; }
    else if (two === 3) { return 1; }
    else { return 0; }
  } else if (one === 2) {
    if (two === 0) { return 2; }
    else if (two === 1) { return 1; }
    else { return 3; }
  } else if (one === 3) {
    if (two === 0) { return 1; }
    else if (two === 1) { return 2; }
    else { return 3; }
  }
}

13
อันที่จริงแล้วมันสามารถอ่านได้น้อยกว่าต้นฉบับและไม่ลดจำนวนงบถ้า ...
Chad

@Chad ความคิดนี้คือการเพิ่มความเร็วของกระบวนการและแม้ว่ามันจะดูน่ากลัว แต่ก็มีการปรับปรุงอย่างง่ายดายถ้าฉันจะเพิ่มการกระทำเพิ่มเติมในอนาคต การพูดแบบนี้ฉันใช้คำตอบก่อนหน้าซึ่งฉันไม่เข้าใจมาก่อนเลย
TomFirth

3
@ TomFirth84 มีเหตุผลใดที่คุณไม่ปฏิบัติตามอนุสัญญาการเข้ารหัสที่เหมาะสมสำหรับคุณ
ylun.ca

@ylun: ฉันได้ลดบรรทัดก่อนที่จะวางลงบน SO ไม่ใช่สำหรับการอ่าน แต่สำหรับพื้นที่สแปมที่แท้จริง มีการฝึกฝนมากมายในหน้านี้และน่าเศร้าที่มันเป็นวิธีที่ฉันเรียนรู้และรู้สึกสบายใจ
TomFirth

2
@ TomFirth84 ฉันไม่คิดว่าจะอัปเดตได้อย่างง่ายดายจำนวนบรรทัดเพิ่มขึ้นอย่างเช่นผลิตภัณฑ์ที่มีจำนวนค่าที่อนุญาต
Andrew Lazarus

0
  1. ใช้ค่าคงที่หรือ enums เพื่อทำให้โค้ดอ่านง่ายขึ้น
  2. ลองแบ่งรหัสออกเป็นฟังก์ชันอื่น ๆ
  3. ลองใช้สมมาตรของปัญหา

นี่คือคำแนะนำว่ามันจะเป็นอย่างไร แต่การใช้ ints ที่นี่ยังคงเป็นสิ่งที่น่าเกลียด:

static final int BLOCK_HIGH = 0;
static final int BLOCK_LOW = 1;
static final int ATTACK_HIGH = 2;
static final int ATTACK_LOW = 3;

public static int fightMath(int one, int two) {
    boolean player1Wins = handleAttack(one, two);
    boolean player2Wins = handleAttack(two, one);
    return encodeResult(player1Wins, player2Wins); 
}



private static boolean handleAttack(int one, int two) {
     return one == ATTACK_HIGH && two != BLOCK_HIGH
        || one == ATTACK_LOW && two != BLOCK_LOW
        || one == BLOCK_HIGH && two == ATTACK_HIGH
        || one == BLOCK_LOW && two == ATTACK_LOW;

}

private static int encodeResult(boolean player1Wins, boolean player2Wins) {
    return (player1Wins ? 1 : 0) + (player2Wins ? 2 : 0);
}

มันจะดีกว่าถ้าจะใช้ชนิดที่มีโครงสร้างสำหรับอินพุตและเอาต์พุต อินพุตมีสองฟิลด์: ตำแหน่งและชนิด (บล็อกหรือการโจมตี) เอาต์พุตยังมีสองฟิลด์: player1Wins และ player2Wins การเข้ารหัสนี้เป็นจำนวนเต็มเดียวทำให้การอ่านรหัสทำได้ยากขึ้น

class PlayerMove {
    PlayerMovePosition pos;
    PlayerMoveType type;
}

enum PlayerMovePosition {
    HIGH,LOW
}

enum PlayerMoveType {
    BLOCK,ATTACK
}

class AttackResult {
    boolean player1Wins;
    boolean player2Wins;

    public AttackResult(boolean player1Wins, boolean player2Wins) {
        this.player1Wins = player1Wins;
        this.player2Wins = player2Wins;
    }
}

AttackResult fightMath(PlayerMove a, PlayerMove b) {
    return new AttackResult(isWinningMove(a, b), isWinningMove(b, a));
}

boolean isWinningMove(PlayerMove a, PlayerMove b) {
    return a.type == PlayerMoveType.ATTACK && !successfulBlock(b, a)
            || successfulBlock(a, b);
}

boolean successfulBlock(PlayerMove a, PlayerMove b) {
    return a.type == PlayerMoveType.BLOCK 
            && b.type == PlayerMoveType.ATTACK 
            && a.pos == b.pos;
}

น่าเสียดายที่ Java ไม่ค่อยดีในการแสดงชนิดข้อมูลเหล่านั้น


-2

แทนที่จะทำอะไรแบบนี้

   public int fightMath(int one, int two) {
    return Calculate(one,two)

    }


    private int Calculate(int one,int two){

    if (one==0){
        if(two==0){
     //return value}
    }else if (one==1){
   // return value as per condtiion
    }

    }

4
คุณเพิ่งสร้างฟังก์ชั่นส่วนตัวที่ถูกปิดโดยสาธารณะ ทำไมไม่ใช้เพียงแค่นี้ในฟังก์ชั่นสาธารณะ?
Martin Ueding

5
และคุณยังไม่ได้ลดจำนวนคำสั่ง if
ชาด
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.