• My first qscan

    From Roy van Rijn@21:1/5 to All on Wed Feb 16 13:22:50 2022
    After watching the tournament last weeked I got curious in Corewars again (thanks Fizmo!): there is one thing high on my wishlist to write/create in Redcode and that is to write a competitive qscan from scratch.

    When first learning to play Corewar it took me a while to understand how the current qscans work. Usually I just copied some existing design.

    When writing a qscan you need the following things:

    1) Scan positions as a contious list of SNE/SEQ instructions
    2) Leave a trace on which locations have been scanned (using {}<>)
    3) Use a single JMP/DJNs that 'encoded' which part of the qscan jumped
    4) Decode the single scan location as fast as possible

    That's a daunting task and when you first start/try to make that all work feels impossible. Everytime you add something, you break something else.

    Writing my own qscan I played around with all kinds of indirections and jumps to make small self-mutating changes that decode to two scan locations.

    The more I tried to optimize I always tend to end up at an existig qscan design. Initially I had a decoder that needed one extra instruction to place the calculated scan position in the correct spot; that would never be good enough so I changed it up
    again. And the more I started to write all the similar tables jumped up, one with three values, another with two, and that single table where only a double decrement can point to.

    Below is the code I ended up with, the tables can be freely placed anywhere in the warrior if you recalculate the numbers. I have a small Java program for this.

    The qscan has 32 scan positions at the moment in 39 lines, when used as below. As an example I've taken my warrior "For John". Something I once quickly made for John Metcalf. It has two papers and I slapped an existing qscan on there. This popular design
    has 33 scans in 41 lines (if you again include the tables). That was my goal to beat.

    My new qscan, two lines shorter, one less scan seems to be able to beat the old design, probably because of better spacing. I've run all permutations for a length 75 cycles and this result made me very happy. Against the original "For John" the new
    design beats it with 1509 wins vs 1401.

    I feel there is more room left in the design, probably more scans can be added, but I don't think longer qscaning is worth it. I'll keep tinkering around with it and probably clean it up a little and use formular instead of raw values in the future.

    Here is the new qscan:

    ;redcode-94nop
    ;name ForJohn.B
    ;author Roy van Rijn
    ;strategy For John with a new qscan
    ;assert 1

    qb1 equ 3216
    qb2 equ 4857
    qb3 equ 6667
    qa1 equ 6275
    qa2 equ 2273
    qc1 equ 2504
    qgap equ 619
    qz equ 103

    qgo
    sne.i qptr + 1912 , qptr+2531
    seq.i <tab , qptr+1809
    jmp dec , }dec ;tab*qz
    sne.i qptr+6325 , qptr+6944
    seq.i <(tab2-1) , qptr+6841
    djn.a dec , {dec ;(tab2-1)*qz
    sne.i qptr+2119 , qptr+2738
    seq.i >(tab2) , qptr+2841
    jmp dec , {dec ;tab2*qz
    sne.i qptr+6701 , qptr+7320
    seq.i <(qbomb+1) , qptr+7217
    jmp dec , }qptr ; (qbomb+1)*qz
    sne.i qptr+3248 , qptr+3867
    seq.i <(qbomb-1) , qptr+3764
    jmp dec , {qptr ; (qbomb-1)*qz
    seq.i qptr+3042 , qptr+3661
    djn.b dec , {qptr ;(--(qbomb-1))*(qz)
    seq.i qptr+103 , qptr+722
    jmp dec+1
    sne.i qptr+4271 , qptr+4890
    seq.i <(qbomb) , qptr+4168
    tab jmp *-7 , qc1 ; also used as qstep
    seq.i qptr+4065 , qptr+4684
    jmp dec,<qbomb
    seq.i qptr+1024 , qptr+1643
    jmp dec , >qptr


    bDist1 equ 394;525;4178
    bDist2 equ 7956;945;3018

    wGo spl 1 , }qb1
    qbomb spl 1 , }qb2
    spl 1 , }qb3

    mov {pap2 , {1
    pBoot1 spl bDist1 , }1218

    mov {pap , {1
    pBoot2 jmp bDist2 , }1131


    for 11
    dat 0 , 0
    rof

    dat }qoff , qa1
    tab2 dat }qoff , qa2

    for 10
    dat 0 , 0
    rof

    iStep equ 1143
    pStep equ 2863;1500
    sStep equ 866;95

    pap2 spl @8 , <pStep
    mov.i }-1 , >-1
    pStone spl #0
    mov bomb , >ptr
    add.x imp , ptr
    ptr jmp imp-iStep*8 , >sStep-6
    bomb dat >1 , }1
    imp mov.i #sStep-1 , iStep

    for 13
    dat 0 , 0
    rof


    pStep1 equ 697;6963
    pStep2 equ 3446;4657
    pStep3 equ 1188;1899

    sStep1 equ 3845;3552
    sStep2 equ 2662;1393
    sStep3 equ 6547;6676

    pap spl @8 , }pStep1
    mov.i }-1 , >-1

    spl @0 , >pStep2
    mov.i }-1 , >-1

    mov.i #sStep1 , {1
    mov.i sStep2 , }sStep3

    mov.i {-4 , <1
    jmz.a @0 , pStep3

    for 7
    dat 0 , 0
    rof

    qoff equ -87
    qstep equ -7
    qtime equ 19

    ; The qscan and decoder can be placed in many positions if you update the tables
    ; In this case this layout has the least jumps through the code


    dec mul.b *qptr , qptr

    zero equ qgo-1

    sne.i zero , @qptr
    add.ab #qgap , qptr
    mov.i tab2 , @1
    qptr mov.i >qbomb , }qz
    sub.ab tab , qptr
    djn -3 , #qtime
    jmp wGo , <855
    end qgo

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Roy van Rijn@21:1/5 to All on Thu Feb 17 03:29:58 2022

    I feel there is more room left in the design, probably more scans can be added, but I don't think longer qscaning is worth it. I'll keep tinkering around with it and probably clean it up a little and use formular instead of raw values in the future.


    And to 'prove the point' that more scans can be added, here is a different version that has 43 lines (if you include the tables) and it scans a whopping 36 positions all spaced at least 100 apart (as good as it gets). However, more scans isn't always
    what you want, the added lines are SEQ/JMP pairs that take up space. It might be possible to squize in another SNE/SEQ/JMP pair, that would probably be useful in this case. But in general, adding more scans doesn't automatically cause a better score,
    because you're adding more lines.


    ;redcode-94nop
    ;name ForJohn.C
    ;author Roy van Rijn
    ;strategy For John with a new qscan
    ;strategy 43 lines, 36 scans
    ;assert 1

    qb1 equ 7379
    qb2 equ 2627
    qb3 equ 6913
    qa1 equ 7598
    qa2 equ 4115
    qc1 equ 4957
    qgap equ 1457
    qz equ 104

    qgo
    sne.i qptr + 3528 , qptr+4985
    seq.i <tab , qptr+3424
    jmp dec , }dec ;tab*qz
    sne.i qptr+6192 , qptr+7649
    seq.i <(tab2-1) , qptr+6088
    djn.a dec , {dec ;(tab2-1)*qz
    sne.i qptr+3960 , qptr+5417
    seq.i >(tab2) , qptr+5521
    jmp dec , {dec ;tab2*qz
    sne.i qptr+6952 , qptr+409
    seq.i <(qbomb+1) , qptr+305
    jmp dec , }qptr ; (qbomb+1)*qz
    sne.i qptr+7416 , qptr+873
    seq.i <(qbomb-1) , qptr+769
    jmp dec , {qptr ; (qbomb-1)*qz
    seq.i qptr+7208 , qptr+665
    djn.b dec , {qptr ;(--(qbomb-1))*(qz)
    seq.i qptr+3730 , qptr+5187
    jmp dec , >qptr
    sne.i qptr+1208 , qptr+2665
    seq.i <(qbomb) , qptr+1104
    tab jmp *-7 , qc1 ; also used as qstep
    seq.i qptr+1000 , qptr+2457
    jmp dec,<qbomb
    seq.i <qptr , qptr+1560
    jmp dec+1
    seq.i qptr+3852, qptr+5309
    jmp dec,<qptr
    seq.i qptr+556,qptr+2013
    djn.f dec,qptr

    bDist1 equ 394;525;4178
    bDist2 equ 7956;945;3018

    wGo spl 1 , }qb1
    qbomb spl 1 , }qb2
    spl 1 , }qb3
    mov {pap2 , {1
    pBoot1 spl bDist1 , }1218

    mov {pap , {1
    pBoot2 jmp bDist2 , }1131


    for 7
    dat 0 , 0
    rof

    dat }qoff , qa1
    tab2 dat }qoff , qa2

    for 10
    dat 0 , 0
    rof

    iStep equ 1143
    pStep equ 2863;1500
    sStep equ 866;95

    pap2 spl @8 , <pStep
    mov.i }-1 , >-1
    pStone spl #0
    mov bomb , >ptr
    add.x imp , ptr
    ptr jmp imp-iStep*8 , >sStep-6
    bomb dat >1 , }1
    imp mov.i #sStep-1 , iStep

    for 13
    dat 0 , 0
    rof

    pStep1 equ 697;6963
    pStep2 equ 3446;4657
    pStep3 equ 1188;1899

    sStep1 equ 3845;3552
    sStep2 equ 2662;1393
    sStep3 equ 6547;6676

    pap spl @8 , }pStep1
    mov.i }-1 , >-1

    spl @0 , >pStep2
    mov.i }-1 , >-1

    mov.i #sStep1 , {1
    mov.i sStep2 , }sStep3

    mov.i {-4 , <1
    jmz.a @0 , pStep3

    for 7
    dat 0 , 0
    rof

    qoff equ -87
    qstep equ -7
    qtime equ 19

    dec mul.b *qptr , qptr

    zero equ qgo-1

    sne.i zero , @qptr
    add.ab #qgap , qptr
    mov.i tab2 , @1
    qptr mov.i >qbomb , }qz
    sub.ab tab , qptr
    djn -3 , #qtime
    jmp wGo , <855


    end qgo

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From inversed@21:1/5 to All on Thu Feb 17 12:13:52 2022
    I'll be publishing the upgraded q^i in the next CoreOps issue. I'll make sure to add your new design to the comparison. So how many different modern qscan types do we have now? q^4.5, this new one, q^i, one from The Art of CoreWar, Paul Kline's - have I
    missed anything?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From John Metcalf@21:1/5 to Roy van Rijn on Thu Feb 17 23:32:08 2022
    On Wed, 16 Feb 2022 13:22:50 -0800, Roy van Rijn wrote:

    I feel there is more room left in the design, probably more scans can be added, but I don't think longer qscaning is worth it. I'll keep
    tinkering around with it and probably clean it up a little and use
    formular instead of raw values in the future.

    It might be possible to add some extra SNE/SEQ/JMP triplets using a
    technique similar to Paul Kline's SlowQscan:

    * https://users.obs.carnegiescience.edu/birk/COREWAR/94/HILL/slowQscan.red

    Have you experimented with the bomber? I was wondering if it's worth
    trying TNT or SPL/JMP bombs, and if the pattern is optimal.

    Also can the bomber be improved against -l 200 warriors on the LP hill?

    On Thu, 17 Feb 2022 12:13:52 -0800, inversed wrote:

    I'll be publishing the upgraded q^i in the next CoreOps issue. I'll make
    sure to add your new design to the comparison. So how many different
    modern qscan types do we have now? q^4.5, this new one, q^i, one from
    The Art of CoreWar, Paul Kline's - have I missed anything?

    There's Q^5 which appears to be an optimized Q^4.5. Also I use a mini-
    Q^4 in my warriors with the code and tables in one block to make it easy
    to copy/paste. It's included in Macro Magic with a macro to calculate
    qfac^-1.

    * https://corewar.co.uk/macromagic.htm

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)