シェルソート

シェル ソート – 数値の配列を事前に組み合わせた挿入ソートの変形です。

挿入ソートがどのように機能するかを覚えておく必要があります。

1.ループは0からループの終わりまで開始されるため、配列は2つの部分に分割されます
2. 左側の部分では、2 番目のループが開始され、要素を右から左に比較します。左側の小さい要素が見つかるまで、右側の小さい要素が削除されます。
3. 両方のループの最後に、ソートされたリストが得られます。

昔々、コンピューター科学者のドナルド シェルは、挿入ソート アルゴリズムを改善する方法を考えていました。彼はまた、最初に配列を 2 サイクルで処理するが、一定の距離を置き、通常の挿入ソート アルゴリズムに変わるまで「コーム」を徐々に減らしていくというアイデアも思いつきました。すべては非常に単純で、落とし穴はありません。上記の 2 つのサイクルに別のサイクルを追加し、「コーム」のサイズを徐々に小さくします。必要なのは、比較するときに配列を超えないよう距離を確認することだけです。

本当に興味深いトピックは、最初のループの各反復で比較長を変更するためのシーケンスを選択することです。アルゴリズムのパフォーマンスがアルゴリズムに依存するという理由から、これは興味深いものです。

既知のオプションと時間計算量の表は、https: //en .wikipedia.org/wiki/Shellsort#Gap_sequences

理想的な距離の計算にはさまざまな人々が関与しており、このトピックは彼らにとって非常に興味深いものだったようです。 Ruby を実行して、最速の sort() アルゴリズムを呼び出すことはできないでしょうか?

一般に、これらの奇妙な人々は、シェル アルゴリズムの「櫛」の距離/ギャップの計算をテーマに論文を書きました。私は彼らの研究結果を単純に使用し、Hibbard、Knuth-Pratt、Chiura、Sedgwick の 5 種類のシーケンスをチェックしました。

import time
import random
from functools import reduce
import math

DEMO_MODE = False

if input("Demo Mode Y/N? ").upper() == "Y":
    DEMO_MODE = True

class Colors:
    BLUE = '\033[94m'
    RED = '\033[31m'
    END = '\033[0m'

def swap(list, lhs, rhs):
    list[lhs], list[rhs] = list[rhs], list[lhs]
    return list

def colorPrintoutStep(numbers: List[int], lhs: int, rhs: int):
    for index, number in enumerate(numbers):
        if index == lhs:
            print(f"{Colors.BLUE}", end = "")
        elif index == rhs:
            print(f"{Colors.RED}", end = "")
        print(f"{number},", end = "")
        if index == lhs or index == rhs:
            print(f"{Colors.END}", end = "")
        if index == lhs or index == rhs:
            print(f"{Colors.END}", end = "")
    print("\n")
    input(">")

def ShellSortLoop(numbers: List[int], distanceSequence: List[int]):
    distanceSequenceIterator = reversed(distanceSequence)
    while distance:= next(distanceSequenceIterator, None):
        for sortArea in range(0, len(numbers)):
            for rhs in reversed(range(distance, sortArea + 1)):
                lhs = rhs - distance
                if DEMO_MODE:
                    print(f"Distance: {distance}")
                    colorPrintoutStep(numbers, lhs, rhs)
                if numbers[lhs] > numbers[rhs]:
                    swap(numbers, lhs, rhs)
                else:
                    break

def ShellSort(numbers: List[int]):
    global ShellSequence
    ShellSortLoop(numbers, ShellSequence)

def HibbardSort(numbers: List[int]):
    global HibbardSequence
    ShellSortLoop(numbers, HibbardSequence)

def ShellPlusKnuttPrattSort(numbers: List[int]):
    global KnuttPrattSequence
    ShellSortLoop(numbers, KnuttPrattSequence)

def ShellPlusCiuraSort(numbers: List[int]):
    global CiuraSequence
    ShellSortLoop(numbers, CiuraSequence)

def ShellPlusSedgewickSort(numbers: List[int]):
    global SedgewickSequence
    ShellSortLoop(numbers, SedgewickSequence)

def insertionSort(numbers: List[int]):
    global insertionSortDistanceSequence
    ShellSortLoop(numbers, insertionSortDistanceSequence)

def defaultSort(numbers: List[int]):
    numbers.sort()

def measureExecution(inputNumbers: List[int], algorithmName: str, algorithm):
    if DEMO_MODE:
        print(f"{algorithmName} started")
    numbers = inputNumbers.copy()
    startTime = time.perf_counter()
    algorithm(numbers)
    endTime = time.perf_counter()
    print(f"{algorithmName} performance: {endTime - startTime}")

def sortedNumbersAsString(inputNumbers: List[int], algorithm) -> str:
    numbers = inputNumbers.copy()
    algorithm(numbers)
    return str(numbers)

if DEMO_MODE:
    maximalNumber = 10
    numbersCount = 10
else:
    maximalNumber = 10
    numbersCount = random.randint(10000, 20000)

randomNumbers = [random.randrange(1, maximalNumber) for i in range(numbersCount)]

ShellSequenceGenerator = lambda n: reduce(lambda x, _: x + [int(x[-1]/2)], range(int(math.log(numbersCount, 2))), [int(numbersCount / 2)])
ShellSequence = ShellSequenceGenerator(randomNumbers)
ShellSequence.reverse()
ShellSequence.pop()

HibbardSequence = [
    0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095,
    8191, 16383, 32767, 65535, 131071, 262143, 524287, 1048575,
    2097151, 4194303, 8388607, 16777215, 33554431, 67108863, 134217727,
    268435455, 536870911, 1073741823, 2147483647, 4294967295, 8589934591
]

KnuttPrattSequence = [
    1, 4, 13, 40, 121, 364, 1093, 3280, 9841, 29524, 88573, 265720, 
    797161, 2391484, 7174453, 21523360, 64570081, 193710244, 581130733, 
    1743392200, 5230176601, 15690529804, 47071589413
]

CiuraSequence = [
            1, 4, 10, 23, 57, 132, 301, 701, 1750, 4376, 
            10941, 27353, 68383, 170958, 427396, 1068491, 
            2671228, 6678071, 16695178, 41737946, 104344866, 
            260862166, 652155416, 1630388541
]

SedgewickSequence = [
            1, 5, 19, 41, 109, 209, 505, 929, 2161, 3905,
            8929, 16001, 36289, 64769, 146305, 260609, 587521, 
            1045505, 2354689, 4188161, 9427969, 16764929, 37730305, 
            67084289, 150958081, 268386305, 603906049, 1073643521, 
            2415771649, 4294770689, 9663381505, 17179475969
]

insertionSortDistanceSequence = [1]

algorithms = {
    "Default Python Sort": defaultSort,
    "Shell Sort": ShellSort,
    "Shell + Hibbard" : HibbardSort,
    "Shell + Prat, Knutt": ShellPlusKnuttPrattSort,
    "Shell + Ciura Sort": ShellPlusCiuraSort,
    "Shell + Sedgewick Sort": ShellPlusSedgewickSort,
    "Insertion Sort": insertionSort
}

for name, algorithm in algorithms.items():
    measureExecution(randomNumbers, name, algorithm)

reference = sortedNumbersAsString(randomNumbers, defaultSort)

for name, algorithm in algorithms.items():
    if sortedNumbersAsString(randomNumbers, algorithm) != reference:
        print("Sorting validation failed")
        exit(1)

print("Sorting validation success")
exit(0)

私の実装では、ランダムな数値セットの場合、最も速いギャップはセジウィックとヒバードです。

マイピー

Python 3 – の静的型付けアナライザーについても触れておきたいと思います。マイピー。動的型付けを使用する言語に固有の問題に対処するのに役立ちます。つまり、不必要な場所に何かが貼り付けられる可能性を排除します。

経験豊富なプログラマーが言うように、「専門家のチームがいる場合、静的型付けは必要ありません」。いつか私たち全員が専門家になり、完全に統一してマシンを理解しながらコードを書くようになりますが、今のところは同様のユーティリティを使用できます。静的型付け言語。

リンク

https://gitlab.com/demensdeum /algorithms/-/tree/master/sortAlgorithms/shellSort
http://mypy-lang.org/

ソース

https://dl.acm.org/doi/10.1145/368370.368387
https://en.wikipedia.org/wiki/Shellsort
http://rosettacode.org/wiki/Sorting_algorithms/Shell_sort
https://ru.wikipedia.org/wiki/Сортировка_Шелла
https://neerc.ifmo.ru/wiki/index.php?title=Сортировка_Шелла
https://twitter.com/gvanrossum/status/700741601966985216

二重選択ソート

Double Selection Sort – 選択ソートのサブタイプで、速度が 2 倍になるようです。バニラのアルゴリズムは、数値のリストを 2 回ループし、最小の数値を見つけて、上のレベルのループが指す現在の数値と位置を交換します。二重選択ソートでは、最小値と最大値を検索し、– より上のレベルでループが指す 2 桁を置き換えます。左右に 2 つの数字。この乱交全体は、置換される数字のカーソルがリストの中央に見つかると終了します。その結果、ソートされた数字が視覚的中心の左右に取得されます。
アルゴリズムの時間計算量は、選択ソートと同様です。 O(n2) ですが、おそらく 30 の加速度があると思われます%。

境界線の状態

この段階ですでに、衝突の瞬間を想像することができます。たとえば、左カーソルの番号 (最小値) がリスト内の最大値を指しているとき、最小値が再配置され、再配置が行われます。最大数がすぐに壊れてしまいます。したがって、アルゴリズムのすべての実装には、そのようなケースのチェックとインデックスの正しいものへの置き換えが含まれます。私の実装では、1 回のチェックで十分でした。

  maximalNumberIndex = minimalNumberIndex;
}

Реализация на Cito

Cito – язык либ, язык транслятор. На нем можно писать для C, C++, C#, Java, JavaScript, Python, Swift, TypeScript, OpenCL C, при этом совершенно ничего не зная про эти языки. Исходный код на языке Cito транслируется в исходный код на поддерживаемых языках, далее можно использовать как библиотеку, либо напрямую, исправив сгенеренный код руками. Эдакий Write once – translate to anything.
Double Selection Sort на cito:

{
    public static int[] sort(int[]# numbers, int length)
    {
        int[]# sortedNumbers = new int[length];
        for (int i = 0; i < length; i++) {
            sortedNumbers[i] = numbers[i];
        }
        for (int leftCursor = 0; leftCursor < length / 2; leftCursor++) {
            int minimalNumberIndex = leftCursor;
            int minimalNumber = sortedNumbers[leftCursor];

            int rightCursor = length - (leftCursor + 1);
            int maximalNumberIndex = rightCursor;
            int maximalNumber = sortedNumbers[maximalNumberIndex];

            for (int cursor = leftCursor; cursor <= rightCursor; cursor++) { int cursorNumber = sortedNumbers[cursor]; if (minimalNumber > cursorNumber) {
                    minimalNumber = cursorNumber;
                    minimalNumberIndex = cursor;
                }
                if (maximalNumber < cursorNumber) {
                    maximalNumber = cursorNumber;
                    maximalNumberIndex = cursor;
                }
            }

            if (leftCursor == maximalNumberIndex) {
                maximalNumberIndex = minimalNumberIndex;
            }

            int fromNumber = sortedNumbers[leftCursor];
            int toNumber = sortedNumbers[minimalNumberIndex];
            sortedNumbers[minimalNumberIndex] = fromNumber;
            sortedNumbers[leftCursor] = toNumber;

            fromNumber = sortedNumbers[rightCursor];
            toNumber = sortedNumbers[maximalNumberIndex];
            sortedNumbers[maximalNumberIndex] = fromNumber;
            sortedNumbers[rightCursor] = toNumber;
        }
        return sortedNumbers;
    }
} 

リンク

https://gitlab.com/demensdeum /algorithms/-/tree/master/sortAlgorithms/doubleSelectionSort
https://github.com/pfusik/cito

ソース

https://www.researchgate.net/publication/330084245_改良_Double_Selection_Sort_using_Algorithm
http://algolab.valemak.com/selection-double
https://www.geeksforgeeks.org/sorting-algorithm-slightly-improves-selection-sort/

カクテルシェーカーの並べ替え

カクテル シェーカー ソート –双方向バブルソーティングの一種であるシェーカーソーティング。
アルゴリズムは次のように機能します。

<オル>

  • ループ内での最初の検索方向が選択されます (通常は左から右)
  • ループの次に、数値がペアでチェックされます
  • 次の要素の方が大きい場合、それらは交換されます
  • 終了すると、検索プロセスが方向を逆にして再び開始されます
  • 検索は順列がなくなるまで繰り返されます
  • アルゴリズムの時間計算量はバブル – に似ています。 O(n2)

    PHP での実装例:

    <?php
    
    function cocktailShakeSort($numbers)
    {
        echo implode(",", $numbers),"\n";
        $direction = false;
        $sorted = false;
        do {
            $direction = !$direction;        
            $firstIndex = $direction == true ? 0 : count($numbers) - 1;
            $lastIndex = $direction == true ? count($numbers) - 1 : 0;
            
            $sorted = true;
            for (
                $i = $firstIndex;
                $direction == true ? $i < $lastIndex : $i > $lastIndex;
                $direction == true ? $i++ : $i--
            ) {
                $lhsIndex = $direction ? $i : $i - 1;
                $rhsIndex = $direction ? $i + 1 : $i;
    
                $lhs = $numbers[$lhsIndex];
                $rhs = $numbers[$rhsIndex];
    
                if ($lhs > $rhs) {
                    $numbers[$lhsIndex] = $rhs;
                    $numbers[$rhsIndex] = $lhs;
                    $sorted = false;
                }
            }
        } while ($sorted == false);
    
        echo implode(",", $numbers);
    }
    
    $numbers = [2, 1, 4, 3, 69, 35, 55, 7, 7, 2, 6, 203, 9];
    cocktailShakeSort($numbers);
    
    ?>

    Ссылки

    https://gitlab.com/demensdeum/algorithms/-/blob/master/sortAlgorithms/cocktailShakerSort/cocktailShakerSort.php

    Источники

    https://www.youtube.com/watch?v=njClLBoEbfI
    https://www.geeksforgeeks.org/cocktail-sort/
    https://rosettacode.org/wiki/Sorting_algorithms/Cocktail_sort

    ファットボーイサイズ –フォルダーとファイルのサイズを表示するユーティリティ

    ファットボーイサイズ –端末内のフォルダーやファイルのサイズを表示するユーティリティ
    です。Python 3 をサポートするあらゆるシステムで動作します。

    実行: python3 fbs.py
    出力モード 1: python3 fbs.py -v
    出力モード 2: python3 fbs.py --version

    ターミナルで現在開いているパスに対してのみ機能します。

    結果の例:
    python3 ~/Sources/my/fatboysize/fbs.py
    .local -> 145.GB
    ダウンロード -> 103.GB
    .キャッシュ -> 37.0GB
    .android -> 11.6GB
    ソース -> 8.63GB

    ご覧のとおり、ダウンロード フォルダーは非常に小さいですが、大きい

    です。

    リンク

    https://gitlab.com/demensdeum/fatboysize/p>

    …And Primus for All

    In this note I will describe the launch of Steam games on the Linux distribution Arch Linux in the configuration of an Intel + Nvidia laptop

    Counter-Strike: Global Offensive

    The only configuration that worked for me is Primus-vk + Vulkan.

    Install the required packages:
    pacman -S vulkan-intel lib32-vulkan-intel nvidia-utils lib32-nvidia-utils vulkan-icd-loader lib32-vulkan-icd-loader primus_vk

    Next, add launch options for Counter-Strike: Global Offensive:
    pvkrun %command% -vulkan -console -fullscreen

    Should work!

    Sid Meier’s Civilization VI

    Works in conjunction – Primus + OpenGL + LD_PRELOAD.

    Install the Primus package:
    pacman -S primus

    Next, add launch options for Sid Meier’s Civilization VI:
    LD_PRELOAD='/usr/lib/libfreetype.so.6:/usr/lib/libbrotlicommon.so.1:/usr/lib/libbrotlidec.so.1' primusrun %command%

    LD_PRELOAD pushes the Freetype compression and font libraries.

    Dota 2

    Works in conjunction – Primus + OpenGL + removal of locks at startup.

    Install the Primus package:
    pacman -S primus

    Next, add launch options for Dota 2:
    primusrun %command% -gl -console

    If the game doesn’t start with fcntl(5) for /tmp/source_engine_2808995433.lock failed, then try deleting the /tmp/source_engine_2808995433.lock file
    rm /tmp/source_engine_2808995433.lock
    Usually the lock file is left over from the last game session unless the game was closed naturally.

    How to check?

    The easiest way to check the launch of applications on a discrete Nvidia graphics card is through the nvidia-smi utility:

    For games on the Source engine, you can check through the game console using the mat_info command:

    References

    https://wiki.archlinux.org/title/Steam/Game-specific_troubleshooting
    https://help.steampowered.com/en/faqs/view/145A-FE54-F37B-278A
    https://bbs.archlinux.org/viewtopic.php?id=277708

    スリープソート

    睡眠並べ替え – sleep sort は、決定論的な奇妙な並べ替えアルゴリズムのもう 1 つの代表です。

    次のように動作します:

    <オル>

  • 要素のリストをループします
  • ループごとに別のスレッドが起動される
  • スレッドは一定期間スリープします –要素の値とスリープ後に出力される値
  • ループの最後で、スレッドの最長スリープが完了するまで待機し、並べ替えられたリストを表示します
  • C での睡眠ソート アルゴリズムのサンプル コード:

    #include <stdlib.h>
    #include <pthread.h>
    #include <unistd.h>
    
    typedef struct {
        int number;
    } ThreadPayload;
    
    void *sortNumber(void *args) {
        ThreadPayload *payload = (ThreadPayload*) args;
        const int number = payload->number;
        free(payload);
        usleep(number * 1000);
        printf("%d ", number);
        return NULL;
    }
    
    int main(int argc, char *argv[]) {
        const int numbers[] = {2, 42, 1, 87, 7, 9, 5, 35};
        const int length = sizeof(numbers) / sizeof(int);
    
        int maximal = 0;
        pthread_t maximalThreadID;
    
        printf("Sorting: ");
        for (int i = 0; i < length; i++) { pthread_t threadID; int number = numbers[i]; printf("%d ", number); ThreadPayload *payload = malloc(sizeof(ThreadPayload)); payload->number = number;
            pthread_create(&threadID, NULL, sortNumber, (void *) payload);
            if (maximal < number) {
                maximal = number;
                maximalThreadID = threadID;
            }
        }
        printf("\n");
        printf("Sorted: ");
        pthread_join(maximalThreadID, NULL);
        printf("\n");
        return 0;
    }
    

    この実装では、マイクロ秒で usleep 関数を使用し、値に 1000 を乗算しました。ミリ秒単位
    で。アルゴリズムの時間計算量 – O(とても長い)

    リンク

    https://gitlab.com/demensdeum /algorithms/-/tree/master/sortAlgorithms/sleepSort

    ソース

    https://codoholicconfessions.wordpress。 com/2017/05/21/strangest-sorting-algorithms/
    https://twitter.com/javascriptdaily/status/856267407106682880?lang=en
    https://stackoverflow.com/questions/6474318/what-is-the-time-complexity-of-the-sleep-sort

    スターリンソート

    スターリン ソート – sort through は、データ損失を伴う並べ替えアルゴリズムの 1 つです。
    このアルゴリズムは非常に生産的効率的で、時間計算量は O(n) です。

    次のように動作します:

    <オル>

  • 配列をループし、現在の要素と次の要素を比較します
  • 次の要素が現在の要素より小さい場合は、それを削除します
  • 結果として、O(n) でソートされた配列が得られます
  • アルゴリズムの出力の例:

    Gulag: [1, 3, 2, 4, 6, 42, 4, 8, 5, 0, 35, 10]
    Element 2 sent to Gulag
    Element 4 sent to Gulag
    Element 8 sent to Gulag
    Element 5 sent to Gulag
    Element 0 sent to Gulag
    Element 35 sent to Gulag
    Element 10 sent to Gulag
    Numbers: [1, 3, 4, 6, 42]
    Gulag: [2, 4, 8, 5, 0, 35, 10]
    

    Python 3 コード:

    gulag = []
    
    print(f"Numbers: {numbers}")
    print(f"Gulag: {numbers}")
    
    i = 0
    maximal = numbers[0]
    
    while i < len(numbers):
        element = numbers[i]
        if maximal > element:
            print(f"Element {element} sent to Gulag")
            gulag.append(element)
            del numbers[i]
        else:
            maximal = element        
            i += 1
    
    print(f"Numbers: {numbers}")
    print(f"Gulag: {gulag}")
    

    欠点としてはデータ損失が挙げられますが、O(n) で理想的なソート済みリストを実現するためには、他にどのような方法があるでしょうか?

    リンク

    https://gitlab.com/demensdeum /algorithms/-/tree/master/sortAlgorithms/stalinSort

    ソース

    https://github.com/gustavo-depaula/stalin-sort
    https://www.youtube.com/shorts/juRL-Xn-E00
    https://www.youtube.com/watch?v=L78i2YcyYfk

    選択範囲の並べ替え

    選択並べ替え –選択ソートアルゴリズム。何を選ぶの?でも最低数!!!
    アルゴリズムの時間計算量 – O(n2)

    アルゴリズムは次のように機能します。

    <オル>

  • 配列を左から右にループ処理し、現在の開始インデックスとインデックスの番号を覚えておきます。番号 A とします
  • ループ内で、別のループを実行して左から右に移動し、A より小さいものを探します
  • 小さい方を見つけたらインデックスを覚えます。今度は小さい方が数字 A になります
  • 内側のループが終了したら、開始インデックスの数値と数値 A を交換します
  • 上のループを完全に通過すると、ソートされた配列が得られます
  • アルゴリズムの実行例:

    (29, 49, 66, 35, 7, 12, 80)
    29 > 7
    (7, 49, 66, 35, 29, 12, 80)
    Round 1 ENDED
    Round 2
    (7, 49, 66, 35, 29, 12, 80)
    49 > 35
    35 > 29
    29 > 12
    (7, 12, 66, 35, 29, 49, 80)
    Round 2 ENDED
    Round 3
    (7, 12, 66, 35, 29, 49, 80)
    66 > 35
    35 > 29
    (7, 12, 29, 35, 66, 49, 80)
    Round 3 ENDED
    Round 4
    (7, 12, 29, 35, 66, 49, 80)
    (7, 12, 29, 35, 66, 49, 80)
    Round 4 ENDED
    Round 5
    (7, 12, 29, 35, 66, 49, 80)
    66 > 49
    (7, 12, 29, 35, 49, 66, 80)
    Round 5 ENDED
    Round 6
    (7, 12, 29, 35, 49, 66, 80)
    (7, 12, 29, 35, 49, 66, 80)
    Round 6 ENDED
    Sorted: (7, 12, 29, 35, 49, 66, 80)
    

    Rosetta コードで Objective-C 実装が見つかりませんでした。 、私は自分でこう書きました。

    #include <Foundation/Foundation.h>
    
    @implementation SelectionSort
    - (void)performSort:(NSMutableArray *)numbers
    {
       NSLog(@"%@", numbers);   
       for (int startIndex = 0; startIndex < numbers.count-1; startIndex++) {
          int minimalNumberIndex = startIndex;
          for (int i = startIndex + 1; i < numbers.count; i++) {
             id lhs = [numbers objectAtIndex: minimalNumberIndex];
             id rhs = [numbers objectAtIndex: i];
             if ([lhs isGreaterThan: rhs]) {
                minimalNumberIndex = i;
             }
          }
          id temporary = [numbers objectAtIndex: minimalNumberIndex];
          [numbers setObject: [numbers objectAtIndex: startIndex] 
                   atIndexedSubscript: minimalNumberIndex];
          [numbers setObject: temporary
                   atIndexedSubscript: startIndex];
       }
       NSLog(@"%@", numbers);
    }
    
    @end 

    Собрать и запустить можно либо на MacOS/Xcode, либо на любой операционной системе поддерживающей GNUstep, например у меня собирается Clang на Arch Linux.
    Скрипт сборки:

            main.m \
            -lobjc \
            `gnustep-config --objc-flags` \
            `gnustep-config --objc-libs` \
            -I /usr/include/GNUstepBase \
            -I /usr/lib/gcc/x86_64-pc-linux-gnu/12.1.0/include/ \
            -lgnustep-base \
            -o SelectionSort \

    Links

    https://gitlab.com/demensdeum/algorithms/-/tree/master/sortAlgorithms/selectionSort

    Sources

    https://rosettacode.org/wiki/Sorting_algorithms/Selection_sort
    https://ru.wikipedia.org/wiki/Сортировка_выбором
    https://en.wikipedia.org/wiki/Selection_sort
    https://www.youtube.com/watch?v=LJ7GYbX7qpM

    カウンティングソート

    カウントソート–カウントソートアルゴリズム。に関しては?はい!そのとおりです!

    アルゴリズムには少なくとも 2 つの配列が含まれます。最初の配列は –ソートする整数のリスト、2 番目 –サイズ = (最大数 – 最小数) + 1 の配列。最初はゼロのみを含みます。次に、最初の配列から数値が並べ替えられ、その数値要素を使用して 2 番目の配列のインデックスが取得され、1 ずつ増分されます。リスト全体を調べた後、最初の配列からの数値の繰り返し数を含む、完全に満たされた 2 番目の配列が得られます。 アルゴリズムには重大なオーバーヘッドがあります – 2 番目の配列には、最初のリストにない数値のゼロも含まれます。メモリによるオーバーヘッド

    2 番目の配列を受け取った後、それを反復処理し、インデックスでソートされた数値を書き込み、カウンターを 0 にデクリメントします。最初は、ゼロ カウンタは無視されます。

    カウントソートアルゴリズムの最適化されていない操作の例:

    <オル>

  • 入力配列 1,9,1,4,6,4,4
  • カウントする配列は 0,1,2,3,4,5,6,7,8,9 になります (最小値は 0、最大値は 9)
  • 合計カウンタが 0,2,0,0,3,0,1,0,0,1 の場合
  • ソートされた配列の合計 1,1,4,4,4,6,9
  • Python 3 のアルゴリズム コード:

    
    numbers = [42, 89, 69, 777, 22, 35, 42, 69, 42, 90, 777]
    
    minimal = min(numbers)
    maximal = max(numbers)
    countListRange = maximal - minimal
    countListRange += 1
    countList = [0] * countListRange
    
    print(numbers)
    print(f"Minimal number: {minimal}")
    print(f"Maximal number: {maximal}")
    print(f"Count list size: {countListRange}")
    
    for number in numbers:
        index = number - minimal
        countList[index] += 1
    
    replacingIndex = 0
    for index, count in enumerate(countList):
        for i in range(count):
            outputNumber = minimal + index
            numbers[replacingIndex] = outputNumber
            replacingIndex += 1
    
    print(numbers)

    Из-за использования двух массивов, временная сложность алгоритма O(n + k)

    Ссылки

    https://gitlab.com/demensdeum/algorithms/-/tree/master/sortAlgorithms/countingSort

    Источники

    https://www.youtube.com/watch?v=6dk_csyWif0
    https://www.youtube.com/watch?v=OKd534EWcdk
    https://en.wikipedia.org/wiki/Counting_sort
    https://rosettacode.org/wiki/Sorting_algorithms/Counting_sort
    https://pro-prof.com/forums/topic/%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC-%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B8-%D0%BF%D0%BE%D0%B4%D1%81%D1%87%D0%B5%D1%82%D0%BE%D0%BC

    ボゴソート

    疑似ソートまたはスワンプ ソート。最も役に立たないソート アルゴリズムの 1 つ。

    次のように動作します。
    1. 入力は数値の配列です
    2. 数字の配列をランダムにシャッフル(シャッフル)する
    3. 配列がソートされているかどうかを確認します
    4. ソートされていない場合、配列は再度シャッフルされます
    5. 配列がランダムに並べ替えられるまで、このすべてのアクションが繰り返されます。

    ご覧のとおり、このアルゴリズムのパフォーマンスはひどいもので、賢い人は O(n * n!) さえも信じています。何年もの間、カオスの神の栄光のためにサイコロを投げることに行き詰まる可能性があります。配列は決してソートされません。それともソートされるかもしれません >

    実装

    TypeScript で実装するには、次の関数を実装する必要がありました。
    1. オブジェクトの配列をシャッフルする
    2. 配列の比較
    3. 0 から数値 (原文どおり!) までの範囲の乱数を生成します
    4. 進行状況を印刷します。並べ替えは延々と続くようです

    以下は TypeScript 実装コードです。

    const randomInteger = (maximal: number) => Math.floor(Math.random() * maximal);
    const isEqual = (lhs: any[], rhs: any[]) => lhs.every((val, index) => val === rhs[index]);
    const shuffle = (array: any[]) => {
        for (var i = 0; i < array.length; i++) { var destination = randomInteger(array.length-1); var temp = array[i]; array[i] = array[destination]; array[destination] = temp; } } let numbers: number[] = Array.from({length: 10}, ()=>randomInteger(10));
    const originalNumbers = [...numbers];
    const sortedNumbers = [...numbers].sort();
    
    let numberOfRuns = 1;
    
    do {
        if (numberOfRuns % 1000 == 0) {
            printoutProcess(originalNumbers, numbers, numberOfRuns);
        }
        shuffle(numbers);
        numberOfRuns++;
    } while (isEqual(numbers, sortedNumbers) == false)
    
    console.log(`Success!`);
    console.log(`Run number: ${numberOfRuns}`)
    console.log(`Original numbers: ${originalNumbers}`);
    console.log(`Current numbers: ${originalNumbers}`);
    console.log(`Sorted numbers: ${sortedNumbers}`);

    Для отладки можно использовать VSCode и плагин TypeScript Debugger от kakumei.

    Как долго

    Вывод работы алгоритма:

    src/bogosort.ts:1
    Still trying to sort: 5,4,8,7,5,0,2,9,7,2, current shuffle 8,7,0,2,4,7,2,5,9,5, try number: 145000
    src/bogosort.ts:2
    Still trying to sort: 5,4,8,7,5,0,2,9,7,2, current shuffle 7,5,2,4,9,8,0,5,2,7, try number: 146000
    src/bogosort.ts:2
    Still trying to sort: 5,4,8,7,5,0,2,9,7,2, current shuffle 0,2,7,4,9,5,7,5,8,2, try number: 147000
    src/bogosort.ts:2
    Still trying to sort: 5,4,8,7,5,0,2,9,7,2, current shuffle 5,9,7,8,5,4,2,7,0,2, try number: 148000
    src/bogosort.ts:2
    Success!
    src/bogosort.ts:24
    Run number: 148798
    src/bogosort.ts:25
    Original numbers: 5,4,8,7,5,0,2,9,7,2
    src/bogosort.ts:26
    Current numbers: 5,4,8,7,5,0,2,9,7,2
    src/bogosort.ts:27
    Sorted numbers: 0,2,2,4,5,5,7,7,8,9

    Для массива из 10 чисел Богосорт перемешивал исходный массив 148798 раз, многовато да?
    Алгоритм можно использовать как учебный, для понимания возможностей языка с которым предстоит работать на рынке. Лично я был удивлен узнав что в ванильных JS и TS до сих пор нет своего алгоритма перемешивания массивов, генерации целого числа в диапазоне, доступа к хэшам объектов для быстрого сравнения.

    Ссылки

    https://gitlab.com/demensdeum/algorithms/-/tree/master/sortAlgorithms/bogosort
    https://www.typescriptlang.org/
    https://marketplace.visualstudio.com/items?itemName=kakumei.ts-debug

    Источники

    https://www.youtube.com/watch?v=r2N3scbd_jg
    https://en.wikipedia.org/wiki/Bogosort

    GoF パターン

    Gang of Four パターンのリスト –面接で不合格になる可能性があるのと同じパターンです。

    生成パターン

    構造パターン

    行動パターン

    パターンインタープリター

    含まれるもの

    インタープリター パターンは、動作デザイン パターンを指します。このパターンを使用すると、AST ツリーを操作して独自のプログラミング言語を実装できます。AST ツリーの頂点は、言語の機能を提供する Interpret メソッドを実装する終端式と非終端式です。

    • 終端式 – 例: 文字列定数 – 「Hello World」
    • 非終端式 – たとえば、Print(“Hello World”) には、Print と終端式 “Hello World” からの引数が含まれます

    違いは何ですか?違いは、解釈は終端式で終了しますが、非終端式の場合は、入力されるすべての頂点/引数にわたって深く継続されることです。 AST ツリーが非終端式のみで構成されている場合、アプリケーションは決して完了しません。どのプロセスにも特定の有限性が必要です。この有限性が終端式の正体であり、通常、終端式には文字列などのデータが含まれます。

    AST ツリーの例を以下に示します。


    Dcoetzee、CC0、ウィキメディア コモンズ経由

    ご覧のとおり、終端式は定数と変数であり、残りは非終端式です。

    含まれないもの

    インタープリタの実装には、AST ツリーに入力された言語文字列の解析は含まれません。終端式と非終端式のクラスを実装し、入力で Context 引数を使用してメソッドを解釈し、式の AST ツリーを作成し、ルート式で Interpret メソッドを実行するだけで十分です。コンテキストを使用して、実行時にアプリケーションの状態を保存できます。

    実装

    パターンには以下が含まれます:

    • クライアント – AST ツリーを返し、ルート ノード (クライアント) に対して Interpret(context) を実行します
    • コンテキスト – アプリケーションの状態が含まれており、解釈時に式に渡されます (コンテキスト)
    • 抽象式 – Interpret(context) (Expression) メソッドを含む抽象クラス
    • ターミナル式は最終式であ​​り、抽象式 (ターミナル式) の子孫です
    • 非終端式は有限式ではありません。これには AST ツリーの深部にある頂点へのポインタが含まれます。通常、非終端式(NonterminalExpression)の解釈の結果に影響します。

    C# でのクライアントの例

            static void Main(string[] args)
            {
                var context = new Context();
                var initialProgram = new PerformExpression(
                    new IExpression[] {
                        new SetExpression("alpha", "1"),
                        new GetExpression("alpha"),
                        new PrintExpression(
                            new IExpression[] {
                                new ConstantExpression("Hello Interpreter Pattern")
                            }
                        )
                    }
                );
                System.Console.WriteLine(initialProgram.interpret(context));
            }
    }
    

    C# での抽象式の例

    {
        String interpret(Context context);
    }
    

    C# のターミナル式の例 (文字列定数)

    {
        private String constant;
    
        public ConstantExpression(String constant) {
            this.constant = constant;
        }
    
        override public String interpret(Context context) {
            return constant;
        }
    }
    

    C# の非終端式の例 (区切り文字「;」を使用して、下位頂点の結果を開始して連結する)

    {
        public PerformExpression(IExpression[] leafs) : base(leafs) {
            this.leafs = leafs;
        }
        
        override public String interpret(Context context) {
            var output = "";
            foreach (var leaf in leafs) {
                output += leaf.interpret(context) + ";";
            }
            return output;
        }
    }
    

    機能的に実行できますか?

    知られているように、すべてのチューリング完全言語は同等です。オブジェクト指向パターンを関数型プログラミング言語に移植することは可能ですか?

    実験として、Elm という Web 用の FP 言語を取り上げてみましょう。 Elm にはクラスはありませんが、レコードとタイプがあるため、次のレコードとタイプが実装に関係します。

    • 式 – 考えられるすべての言語式のリスト (式)
    • 従属式 – 非終端式 (ExpressionLeaf) に従属する式
    • コンテキスト – アプリケーションの状態 (コンテキスト) を保存するレコード
    • Interpret(context) メソッドを実装する関数 – ターミナル式と非ターミナル式の機能を実装する必要なすべての関数
    • インタープリターの状態の補助レコード – インタープリターの正しい動作に必要で、インタープリターの状態とコンテキストを保存します

    Elm で使用可能な式のセット全体の解釈を実装する関数の例:

      case input.expression of
        Constant text ->
          { 
            output = text, 
            context = input.context 
          }
        Perform leafs ->
          let inputs = List.map (\leaf -> { expressionLeaf = leaf, context = input.context } ) leafs in
            let startLeaf = { expressionLeaf = (Node (Constant "")), context = { variables = Dict.empty } } in
              let outputExpressionInput = List.foldl mergeContextsAndRunLeafs startLeaf inputs in
                {
                  output = (runExpressionLeaf outputExpressionInput).output,
                  context = input.context
                }
        Print printExpression ->
          run 
          { 
            expression = printExpression, 
            context = input.context 
          }
        Set key value ->
          let variables = Dict.insert key value input.context.variables in
          {
            output = "OK",
            context = { variables = variables }
          }
        Get key ->
          {
            output = Maybe.withDefault ("No value for key: " ++ key) (Dict.get key input.context.variables),
            context = input.context
          }
    

    解析についてはどうですか?

    ソース コードを AST ツリーに解析することはインタープリター パターンには含まれていません。ソース コードを解析するにはいくつかの方法がありますが、それについてはまた別の機会に説明します。
    Elm のインタープリタの実装では、頂点の解析と下位頂点の解析という 2 つの関数で構成される単純なパーサーを AST ツリーに作成しました。

    parseLeafs state =
        let tokensQueue = state.tokensQueue in
            let popped = pop state.tokensQueue in
                let tokensQueueTail = tail state.tokensQueue in
                    if popped == "Nothing" then
                        state
                    else if popped == "Perform(" then
                        {
                            tokensQueue = tokensQueue,
                            result = (state.result ++ [Node (parse tokensQueue)])
                        }
                    else if popped == ")" then
                        parseLeafs {
                            tokensQueue = tokensQueueTail,
                            result = state.result
                        }
                    else if popped == "Set" then
                        let key = pop tokensQueueTail in
                            let value = pop (tail tokensQueueTail) in
                                parseLeafs {
                                    tokensQueue = tail (tail tokensQueueTail),
                                    result = (state.result ++ [Node (Set key value)])
                                }
                    else if popped == "Get" then
                        let key = pop tokensQueueTail in
                            parseLeafs {
                                tokensQueue = tail tokensQueueTail,
                                result = (state.result ++ [Node (Get key)])
                            }
                    else 
                        parseLeafs {
                            tokensQueue = tokensQueueTail,
                            result = (state.result ++ [Node (Constant popped)])
                        }
    
    parse tokensQueue =
        let popped = pop tokensQueue in
            let tokensQueueTail = tail tokensQueue in
                if popped == "Perform(" then
                    Perform (
                        parseLeafs {
                            tokensQueue = tokensQueueTail, 
                            result = []
                        }
                    ).result
                else if popped == "Set" then
                    let key = pop tokensQueueTail in
                        let value = pop (tail tokensQueueTail) in
                            Set key value
                else if popped == "Print" then
                    Print (parse tokensQueueTail)
                else
                    Constant popped
    

    リンク

    https://gitlab.com/demensdeum /patterns/-/tree/master/interpreter/elm
    https://gitlab.com/demensdeum/patterns/-/tree/master/interpreter/csharp

    ソース

    https://en.wikipedia.org/wiki/Interpreter_pattern
    https://elm-lang.org/
    https://docs.microsoft.com/en-us/dotnet/csharp/

    RGB画像をグレーに

    この投稿では、RGB バッファをグレー (グレースケール) に変換するアルゴリズムについて説明します。
    これは非常に簡単に行われ、バッファの各ピクセル カラー チャネルが特定の式に従って変換され、出力はグレーの画像になります。
    平均的な方法:

    red = average;
    green = average;
    blue = average;

    Складываем 3 цветовых канала и делим на 3.

    Однако существует еще один метод – метод средневзвешенный, он учитывает цветовосприятие человека:

    red = luminance;
    green = luminance;
    blue = luminance;

    Какой метод лучше использовать? Да какой вам больше подходит для конкретной задачи. Далее сравнение методов с помощью тестовой цветовой сетки:

    Пример реализации на JavaScript + HTML 5

        image,
        canvas,
        weightedAverage
    ) {
        const context = canvas.getContext('2d');
    
        const imageWeight = image.width;
        const imageHeight = image.height;
    
        canvas.width = imageWeight;
        canvas.height = imageHeight;
    
        context.drawImage(image, 0, 0);
    
        let pixels = context
            .getImageData(
                0,
                0,
                imageWeight,
                imageHeight
            );
    
        for (let y = 0; y & lt; pixels.height; y++) {
            for (let x = 0; x & lt; pixels.width; x++) {
                const i = (y * 4) * pixels.width + x * 4;
    
                let red = pixels.data[i];
                let green = pixels.data[i + 1];
                let blue = pixels.data[i + 2]
    
                const average = (red + green + blue) / 3;
                const luminance = 0.2126 * red +
                    0.7152 * green +
                    0.0722 * blue;
    
                red = weightedAverage ? luminance : average;
                green = weightedAverage ? luminance : average;
                blue = weightedAverage ? luminance : average;
    
                pixels.data[i] = red;
                pixels.data[i + 1] = green;
                pixels.data[i + 2] = blue;
            }
        }
        context
            .putImageData(
                pixels,
                0,
                0,
                0,
                0,
                pixels.width,
                pixels.height
            );
    }

    Источники

    https://www.baeldung.com/cs/convert-rgb-to-grayscale
    https://twitter.com/mudasobwa/status/1528046455587495940
    https://rosettacode.org/wiki/Grayscale_image

    Ссылки

    http://papugi.demensdeum.repl.co/

    Благодарности

    Спасибо Aleksei Matiushkin (https://twitter.com/mudasobwa) за наводку на Rosetta Code

    Macbook M1 で CSGO を実行する方法

    Macbook M1 で CSGO の起動時に SDL_GetDesktopDisplayMode_REAL エラーが発生した場合は、以下の手順を実行してください。
    1. CSGO の起動オプションを Steam に追加します。
    <コード>-w 1440 -h 900 -全画面

    2.Steam経由でCSGOを起動
    3. エラー SDL_GetDesktopDisplayMode_REAL
    を [無視] または [常に無視] をクリックします。4. 楽しんでください

    チューリング コンピューティング マシン

    私はあなたの注意を引くために、アラン・チューリングの記事「–」の最初のページの翻訳を提示します。 「解像度問題への適用による計算可能な数値について」 1936年。最初の章には、後に現代のコンピューティングの基礎となるコンピューターについての説明が含まれています。

    この記事と説明の完全な翻訳は、アメリカの普及者チャールズ ペッツォルトによる「チューリングを読む」というタイトルの本で読むことができます。計算可能性とチューリング マシンに関するチューリングの画期的な論文を巡る旅 -#8221; (ISBN 978-5-97060-231-7、978-0-470-22905-7)

    元の記事:
    https://www.astro.puc.cl/~rparra/tools/PAPERS/turing_1936.pdf

    解像度問題への適用による計算可能な数値について

    A. M. チューリング

    [1936 年 5 月 28 日に受領 – 1936 年 11 月 12 日に読了]

    計算可能な数値は、小数としての表現が有限の方法で計算できる実数として簡単に説明できます。一見すると、この記事では数値を計算可能なものとして扱いますが、整変数、実数変数、計算可能な変数、計算可能な述語などの計算可能な関数を定義して調査することは、ほぼ同じくらい簡単です。ただし、これらの計算可能なオブジェクトに関連する基本的な問題は、どの場合でも同じです。詳細な検討のために、考慮する方法が最も面倒ではないため、計算可能なオブジェクトとして計算可能な数値を選択しました。計算可能な数値と計算可能な関数などの関係については、近いうちに説明したいと思っています。同時に、計算​​可能な数値で表現される実変数の関数理論の分野でも研究が行われます。私の定義によれば、実数は、小数としての表現が機械で記述できる場合に計算可能です。

    段落 9 と 10 では、計算可能な数値には、当然計算可能であると考えられるすべての数値が含まれることを示すために、いくつかの議論を示します。特に、いくつかの大きなクラスの数値が計算可能であることを示します。これらには、たとえば、すべての代数的数値の実部、ベッセル関数のゼロの実部、数値 π、e などが含まれます。ただし、次の計算不可能な定義可能な数値の例でわかるように、計算可能な数値には、定義可能な数値がすべて含まれるわけではありません。

    計算可能な数値のクラスは非常に大きく、多くの点で実数のクラスと似ていますが、依然として数えることが可能です。 §8 では、反対と思われる特定の議論を検討します。これらの議論の 1 つが正しく適用されると、一見するとゲーデル* の結論と似た結論が導き出されます。これらの結果は非常に重要な応用分野を持っています。特に、以下 (§11) に示すように、解像度の問題は解決できません。

    アロンゾ チャーチ** は、最近の記事で「実効計算可能性」という考え方を紹介しました。これは、私の「計算可能性」という考え方と同じですが、定義はまったく異なります。チャーチも解決の問題に関して同様の結論に達しています。 「計算可能性」と「効率的に数えられる能力」が同等であることの証明は、この記事の付録に記載されています。

    1.コンピュータ

    計算可能な数値とは、小数点以下の桁が有限の方法で数えられる数値であることはすでに述べました。ここではより明確な定義が必要です。この記事では、§9 に到達するまで、ここで与えられた定義を正当化する実際の試みは行いません。現時点では、(このための)(論理的な)根拠は、人間の記憶には必然的に限界があるということだけを述べておきます。

    実数を計算している人間と、有限数の条件 q1、q2、…、qR のみを満たすことができるマシンを比較してみましょう。これらの条件を「m 構成」と呼びます。この(つまり、そのように定義された)マシンには「テープ」(紙に似たもの)が装備されています。機械内を通過するこのベルトはいくつかのセクションに分かれています。それらを「正方形」と呼びましょう。このような各正方形には、何らかの「シンボル」を含めることができます。いつでも、「このマシン内にある」シンボルを含むそのような正方形は 1 つだけ、たとえば r 番目の正方形だけです。このような正方形を「スキャンされたシンボル」と呼びます。 「スキャンされた文字」は、いわばマシンが「直接認識している」唯一の文字です。ただし、m 構成を変更することで、マシンは以前に「見た」(スキャンした) 文字の一部を効果的に記憶できるようになります。いつでもマシンの可能な動作は、m 構成 qn とスキャンされたシンボル *** によって決まります。このシンボルのペアを qn、「構成」と呼びましょう。このように指定された構成によって、特定のマシンの可能な動作が決まります。スキャンされた正方形が空白である (つまり、文字が含まれていない) 構成の一部では、マシンはスキャンされた正方形に新しい文字を書き込み、その他の構成ではスキャンされた文字を消去します。このマシンは移動して別のマスをスキャンすることもできますが、この方法では右または左に隣接するマスにのみ移動できます。これらの操作に加えて、マシンの m 構成を変更することもできます。この場合、書かれた文字の一部は、計算される実数の小数部分である一連の数字を形成します。残りは「記憶を助ける」ための不正確なマークにすぎません。この場合、上記の不正確なマークのみを消去できます。

    ここで考慮される操作には、計算で使用されるすべての操作が含まれると主張します。このステートメントの理論的根拠は、機械理論を理解している読者にとっては理解しやすいものです。したがって、次のセクションでは、「機械」、「テープ」、「スキャンされた」などの用語の意味の理解に基づいて、問題の理論を展開していきます

    *ゲーデル「プリンキピア数学 (1910 年、1912 年、1913 年にホワイトヘッドとラッセルによって出版) および関連システムの形式的に決定不可能な文について、第 1 部」Journal of Mathematics。物理学、ドイツ語月刊誌第 38 号 (1931 年、173-198 ページ。
    ** アロンゾ チャーチ、「初等整数理論における決定不可能な問題」、American J. of Math.、第 58 号 (1936 年)、345-363 ページ。
    *** アロンゾ チャーチ、「解決の問題に関するメモ」、J. of Symbolic Logic、No. 1 (1936)、40-41 ページ

    チューリング爆弾

    1936 年、科学者のアラン チューリングは、著書『計算可能な数について、現実の問題への応用』の中で、数学における可解性の問題に終止符を打つ可能性のある万能計算機の使用について説明しました。その結果、彼は、そのようなマシンの作業結果が反転され、それ自体でループバックされる場合、そのようなマシンは何も正しく解決できないだろうという結論に達しました。 *理想的な* アンチウイルス、*理想的な* タイル セッター、クラッシュに対して理想的なフレーズを提案するプログラムなどを作成するのは不可能であることが判明しました。パラドックス!

    しかし、この汎用コンピューティング マシンはあらゆるアルゴリズムの実装に使用でき、英国諜報機関はそれを利用してチューリングを雇い、第二次世界大戦中にドイツのメッセージを解読するための「ボンベ」マシンの作成を許可しました。

    以下は、元のドキュメントに基づいた、Dart 言語による単一テープ コンピューターの OOP モデリングです。

    チューリング マシンはセクションに分割されたフィルムで構成されており、各セクションにはシンボルが含まれており、シンボルは読み書きできます。映画クラスの例:

    final _map = Map<int, String>(); 
    
      String read({required int at}) { 
        return _map[at] ?? ""; 
      } 
    
      void write({required String symbol, required int at}) { 
        _map[at] = symbol; 
      } 
    }
    

    「スキャン スクエア」もあり、フィルム上を移動して情報を読み書きすることができます (現代語で言えば –)。磁気ヘッド。磁気ヘッド クラスの例:

      int _index = 0; 
      InfiniteTape _infiniteTape; 
      TapeHead(this._infiniteTape) {} 
    
      String next() { 
        _index += 1; 
        move(to: _index); 
        final output = read(); 
        return output; 
      } 
    
      String previous() { 
        _index -= 1; 
        move(to: _index); 
        final output = read(); 
        return output; 
      } 
    
      void move({required int to}) { 
        this._index = to; 
      } 
    
      String read() { 
        return _infiniteTape.read(at: this._index); 
      } 
    
      void write(String symbol) { 
        _infiniteTape.write(symbol: symbol, at: this._index); 
      } 
    
      int index() { 
        return _index; 
      } 
    } 
    

    マシンには、次に何を行うかを決定できる「m 構成」が含まれています。現代語で–状態と状態ハンドラー。状態ハンドラーの例:

      FiniteStateControlDelegate? delegate = null; 
    
      void handle({required String symbol}) { 
        if (symbol == OPCODE_PRINT) { 
          final argument = delegate?.nextSymbol(); 
          print(argument);
        } 
        else if (symbol == OPCODE_GENERATE_RANDOM_NUMBER_FROM_ZERO_TO_AND_WRITE_AFTER) { 
          final to = int.tryParse(delegate!.nextSymbol())!; 
          final value = new Random().nextInt(to); 
          delegate!.nextSymbol(); 
          delegate!.write(value.toString()); 
        } 
        else if (symbol == OPCODE_INPUT_TO_NEXT) { 
          final input = stdin.readLineSync()!; 
          delegate?.nextSymbol(); 
          delegate?.write(input); 
        } 
        else if (symbol == OPCODE_COPY_FROM_TO) { 
          final currentIndex = delegate!.index(); 
    
    и т.д. 
    

    この後、「構成」を作成する必要があります。現代の言葉で言うと、これらはオペレーション コード (オペコード) とそのハンドラーです。オペコードの例:

    const OPCODE_PRINT = "print"; 
    const OPCODE_INCREMENT_NEXT = "increment next"; 
    const OPCODE_DECREMENT_NEXT = "decrement next"; 
    const OPCODE_IF_PREVIOUS_NOT_EQUAL = "if previous not equal"; 
    const OPCODE_MOVE_TO_INDEX = "move to index"; 
    const OPCODE_COPY_FROM_TO = "copy from index to index"; 
    const OPCODE_INPUT_TO_NEXT = "input to next"; 
    const OPCODE_GENERATE_RANDOM_NUMBER_FROM_ZERO_TO_AND_WRITE_AFTER = "generate random number from zero to next and write after"; 
    

    オペコードと停止ハンドラーを作成することを忘れないでください。そうしないと、解決の問題を証明できないか、証明できないことになります。

    次に、「メディエーター」パターンを使用して、チューリング マシン クラス内のすべてのクラスを接続し、クラスのインスタンスを作成し、テープ レコーダーでプログラムを録音し、テープをロードすると、使用できるようになります。

    私個人にとって、何が主要であるかという問題は依然として興味深いものでした –万能計算機の作成、または「Entscheidungsproblem」の証明、その結果副産物として計算機が登場しました。

    カセット

    楽しみのために、自分のバージョンのマシン用にいくつかのカセット プログラムを録音しました。

    ハローワールド

    hello world 
    stop

    Считаем до 16-ти

    0
    if previous not equal
    16
    copy from index to index
    1
    8
    print
    ?
    move to index
    0
    else
    copy from index to index
    1
    16
    print
    ?
    print
    Finished!
    stop

    Самой интересной задачей было написание Quine программы, которая печатает свой исходный код, для одноленточной машины. Первые 8 часов мне казалось что эта задача не решаема с таким малым количеством опкодов, однако всего через 16 часов оказалось что я был не прав.

    Реализация и примеры кассет, источники ниже.

    Ссылки

    https://gitlab.com/demensdeum/turing-machine

    Источники

    https://www.astro.puc.cl/~rparra/tools/PAPERS/turing_1936.pdf
    https://kpolyakov.spb.ru/prog/turing.htm
    https://www.youtube.com/watch?v=dNRDvLACg5Q
    https://www.youtube.com/watch?v=jP3ceURvIYc
    https://www.youtube.com/watch?v=9QCJj5QzETI
    https://www.youtube.com/watch?v=HeQX2HjkcNo&t=0s

    Sega Genesis #5 のアセンブリでの書き込み

    このメモでは、ジョイスティックの読み取り、スプライトの位置の変更、水平方向の反転、Sega Genesis エミュレータ、および場合によってはコンソール自体のプロセスについて説明します。

    将棋ジョイスティックのクリックの読み取りと「イベント」の処理は、次のスキームに従って行われます。

    <オル>

  • 押されたボタンのビットの組み合わせをリクエストする
  • 押されたボタンのビットを読み取る
  • ゲームロジックレベルでの処理
  • スケルトン スプライトを移動するには、現在の位置の変数を保存する必要があります。

    RAM

    ゲーム ロジック変数は RAM に保存されます。これより優れたものはまだ見つかっていません。変数アドレスを宣言して、レンダリング コードを変更しましょう。

    skeletonYpos = $FF0002 
    frameCounter = $FF0004 
    skeletonHorizontalFlip = $FF0006
    
        move.w #$0100,skeletonXpos 
        move.w #$0100,skeletonYpos 
        move.w #$0001,skeletonHorizontalFlip 
    
    FillSpriteTable: 
        move.l #$70000003,vdp_control_port 
        move.w skeletonYpos,vdp_data_port  
        move.w #$0F00,vdp_data_port 
        move.w skeletonHorizontalFlip,vdp_data_port 
        move.w skeletonXpos,vdp_data_port 
    

    ご覧のとおり、作業に利用できるアドレスは 0xFF0000 で始まり 0xFFFFFF で終わり、合計 64 KB のメモリが利用可能です。スケルトンの位置は、skeletonXpos、skeletonYpos で宣言され、水平方向の反転は、skeletonhorizo​​ntalflip で宣言されます。

    ジョイパッド

    VDP と同様に、ジョイパッドの操作は 2 つのポートを介して個別に行われます。制御ポートとデータ ポート。最初のポートは 0xA10009 と 0xA10003 の共通番号です。ジョイパッドを使用する場合、興味深い機能が 1 つあります。まず、ポーリング用のボタンの組み合わせをリクエストする必要があります。次に、バス上の更新を待った後、必要なボタンの押し方を読み取ります。 C/B および D パッド ボタンの場合、これは 0x40 です。以下の例:

      move.b #$40,joypad_one_control_port; C/B/Dpad 
      nop ; bus sync 
      nop ; bus sync 
      move.b joypad_one_data_port,d2 
      rts 
    

    レジスタ d2 では、ボタンが押されたか押されなかったかの状態が残ります。一般に、日付ポートを通じて要求された内容が残ります。その後、お気に入りのエミュレータの Motorola 68000 レジスタ ビューアに移動し、キーストロークに応じて d2 レジスタが何に等しいかを確認します。賢明な方法では、マニュアルでそれを知ることができますが、私たちは彼らの言葉を鵜呑みにしません。 d2

    レジスタで押されたボタンのさらなる処理

        cmp #$FFFFFF7B,d2; handle left 
        beq MoveLeft  
        cmp #$FFFFFF77,d2; handle right  
        beq MoveRight  
        cmp #$FFFFFF7E,d2; handle up  
        beq MoveUp  
        cmp #$FFFFFF7D,d2; handle down  
        beq MoveDown  
        rts

    Проверять нужно конечно отдельные биты, а не целыми словами, но пока и так сойдет. Теперь осталось самое простое – написать обработчики всех событий перемещения по 4-м направлениям. Для этого меняем переменные в RAM, и запускаем процедуру перерисовки.

    Пример для перемещения влево + изменение горизонтального флипа:

        move.w skeletonXpos,d0 
        sub.w #1,d0 
        move.w d0,skeletonXpos 
        move.w #$0801,skeletonHorizontalFlip 
        jmp FillSpriteTable

    После добавления всех обработчиков и сборки, вы увидите как скелет перемещается и поворачивается по экрану, но слишком быстро, быстрее самого ежа Соника.

    Не так быстро!

    Чтобы замедлить скорость игрового цикла, существуют несколько техник, я выбрал самую простую и не затрагивающую работу с внешними портами – подсчет цифры через регистр пока она не станет равна нулю.

    Пример замедляющего цикла и игрового цикла:

      move.w #512,frameCounter 
    WaitFrame: 
      move.w frameCounter,d0 
      sub.w #1,d0 
      move.w d0,frameCounter 
      dbra d0,WaitFrame 
    GameLoop: 
      jsr ReadJoypad 
      jsr HandleJoypad 
      jmp GameLoop 
    

    この後、スケルトンの実行速度が遅くなりますが、これは必要なことです。私が知っているように、「速度を落とす」ための最も一般的なオプションは、垂直同期フラグをカウントすることです。これにより、画面が描画された回数をカウントできるため、特定の fps に関連付けられます。

    リンク

    https://gitlab .com/demensdeum/segagenesisamples/-/blob/main/8Joypad/vasm/main.asm

    ソース

    https://www.chibiakumas.com/68000/platform2.php
    https://huguesjohnson.com/programming/genesis/tiles-sprites/

    Sega Genesis #4 のアセンブリでの作成

    この投稿では、Sega Genesis コンソールの VDP エミュレータを使用してスプライトを描画する方法を説明します。
    スプライトのレンダリングのプロセスは、タイルのレンダリングと非常に似ています。

    <オル>

  • カラーを CRAM にロードする
  • スプライト 8×8 の一部を VRAM にアップロードする
  • VRAM にスプライト テーブルを埋める
  • たとえば、32×32 ピクセルの剣を持ったスケルトンのスプライトを考えてみましょう。

    Skeleton Guy [Animated] by Disthorn

    クラム

    ImaGenesis を使って、アセンブラ用の CRAM カラーと VRAM パターンに変換してみましょう。この後、asm形式のファイルを2つ取得し、色をワードサイズに書き換え、タイルを正しい順序で配置して描画する必要があります
    興味深い情報: 0xF レジスタを介して VDP 自動インクリメントをワード サイズに切り替えることができます。これにより、CRAM カラー フィル コードからアドレス インクリメントが削除されます。

    VRAM

    将棋マニュアルには、大きなスプライトに対する正しい牌の順序が記載されていますが、私たちはより賢いので、ブログからインデックスを取得します。ちび悪魔、インデックス 0 から数え始めましょう:

    0 4 8 12

    1 5 9 13

    2 6 10 14

    3 7 11 15

    なぜすべてが逆さまなのですか?どうしたものか、コンソールは日本語です!右から左でも構いません
    よ!asm スプライト ファイル内の順序を手動で変更してみましょう:

    	dc.l	$11111111	; Tile #0 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111	; Tile #4 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111
    	dc.l	$11111111	; Tile #8 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111122 
    	dc.l	$11111122 
    	dc.l	$11111166 
    	dc.l	$11111166	; Tile #12 
    	dc.l	$11111166 
    	dc.l	$11111166 
    	и т.д. 
    

    通常のタイル/パターンと同様にスプライトを読み込みます:

      lea Sprite,a0 
      move.l #$40200000,vdp_control_port; write to VRAM command 
      move.w #128,d0 ; (16*8 rows of sprite) counter 
    SpriteVRAMLoop: 
      move.l (a0)+,vdp_data_port; 
      dbra d0,SpriteVRAMLoop 
    

    スプライトを描画するには、スプライト テーブルに記入するだけです。

    スプライト テーブル

    スプライト テーブルは VRAM に入力され、その位置のアドレスが VDP レジスタ 0x05 に入力されます。アドレスはこれも複雑です。アドレス F000 の例はマニュアルで確認できます。

    Ок, теперь запишем наш спрайт в таблицу. Для этого нужно заполнить “структуру” данных состоящую из четырех word. Бинарное описание структуры вы можете найти в мануале. Лично я сделал проще, таблицу спрайтов можно редактировать вручную в эмуляторе Exodus.
    構造パラメータは名前から明らかです (例: XPos、YPos)。座標、タイル –描画開始タイルの番号、HSize、VSize –パーツ 8×8、HFlip、VFlip – を追加することでスプライト サイズを変更します。ハードウェアによるスプライトの水平方向と垂直方向の回転

    スプライトは画面外にある可能性があることを覚えておくことが非常に重要です。これは正しい動作です。なぜなら…オフスクリーン スプライトをメモリからアンロード –非常にリソースを大量に消費するアクティビティ
    エミュレータにデータを入力した後、VRAM からアドレス 0xF000 にデータをコピーする必要があります。Exodus はこの機能もサポートしています。
    タイルの描画と同様に、まず VDP 制御ポートにアクセスしてアドレス 0xF000 への書き込みを開始し、次に構造をデータ ポートに書き込みます。
    VRAM アドレス指定の説明はマニュアルまたはブログ 名前のないアルゴリズム

    簡単に言うと、VDP アドレス指定は次のように機能します。
    [..DC BA98 7654 3210 …。 …。 …。 ..FE]
    ここで、hex は目的のアドレスのビット位置です。最初の 2 ビットは、要求されているコマンドのタイプです (例: 01 –)。 VRAMに書き込みます。次に、アドレス 0XF000 については次のようになります。
    0111 0000 0000 0000 0000 0000 0000 0011 (70000003)

    その結果、次のコードが得られます。

      move.l #$70000003,vdp_control_port 
      move.w #$0100,vdp_data_port 
      move.w #$0F00,vdp_data_port 
      move.w #$0001,vdp_data_port 
      move.w #$0100,vdp_data_port 
    

    この後、スケルトン スプライトは座標 256、256 に表示されます。いいですね?

    リンク

    https://gitlab.com/demensdeum /segagenesissamples/-/tree/main/7Sprite/vasm
    https://opengameart.org/content/skeleton-guy-animated

    ソース

    https://namelessalgorithm.com/genesis/blog/vdp/https://www.chibiakumas.com/68000/platform3.php#LessonP27
    https://plutiedev.com/sprites

    Sega Genesis #3 のアセンブリでの書き込み

    この記事では、アセンブラを使用して Sega Genesis エミュレータでタイルから画像を表示する方法を説明します。
    Exodus エミュレータのDemens Deum スプラッシュ画像は次のようになります:

    タイルを使用して PNG 画像を出力するプロセスは、段階的に実行されます。

    <オル>

  • 画像を将棋画面サイズに縮小する
  • PNG を色とタイルに分けたアセンブリ データ コードに変換します
  • カラー パレットを CRAM にロードする
  • タイル/パターンを VRAM にロードする
  • VRAM のプレーン A/B アドレスにタイル インデックスをロードする
  • Blender などのお気に入りのグラフィック エディタを使用して、画像を将棋画面のサイズに縮小できます。
  • PNG 変換

    画像を変換するには、ImaGenesis ツールを使用できます。wine で作業するには、Visual Basic 6 ライブラリが必要です。これらのライブラリは、 winetricks (winetricks vb6run) を使用してインストールするか、インターネットから RICHTX32.OCX をダウンロードして配置できます。正しく動作するためにアプリケーション フォルダーを参照してください。

    ImaGenesis では、4 ビット カラーを選択し、カラーとタイルを 2 つのアセンブラ形式ファイルにエクスポートする必要があります。次に、色を含むファイルで、各色を単語 (2 バイト) に入れる必要があります。これには、dc.w オペコードを使用します。

    CRAM スプラッシュ画面の例:

      dc.w $0000 
      dc.w $0000 
      dc.w $0222 
      dc.w $000A 
      dc.w $0226 
      dc.w $000C 
      dc.w $0220 
      dc.w $08AA 
      dc.w $0446 
      dc.w $0EEE 
      dc.w $0244 
      dc.w $0668 
      dc.w $0688 
      dc.w $08AC 
      dc.w $0200 
      dc.w $0000 
    

    タイル ファイルはそのままにしておきます。このファイルには、読み込むための正しい形式がすでに含まれています。タイル ファイルの一部の例:

    	dc.l	$11111111	; Tile #0 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111	; Tile #1 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    

    上の例からわかるように、タイルは CRAM カラー パレット インデックスで構成される 8×8 グリッドです。

    CRAM の色

    CRAM へのロードは、制御ポート (vdp 制御) への特定の CRAM アドレスへのカラー ロード コマンドを設定することによって行われます。コマンドの形式は Sega Genesis ソフトウェア マニュアル (1989 年) に説明されています。次の色に移動するには、アドレスに 0x20000 を追加するだけであることを付け加えておきます。

    次に、カラーをデータ ポート (vdp データ) にロードする必要があります。読み込みを理解する最も簡単な方法は、以下の例を使用することです。

        lea Colors,a0 ; pointer to Colors label 
        move.l #15,d7; colors counter 
    VDPCRAMFillLoopStep: 
        move.l  d0,vdp_control_port ;  
        move.w  (a0)+,d1; 
        move.w  d1,(vdp_data_port); 
        add.l #$20000,d0 ; increment CRAM address 
        dbra d7,VDPCRAMFillLoopStep 
    

    VRAM 内のタイル

    次は、タイル/パターンを VRAM ビデオ メモリにロードします。これを行うには、VRAM 内のアドレス (たとえば、0x00000000) を選択します。 CRAM と同様に、VRAM に書き込むコマンドと開始アドレスを使用して VDP 制御ポートに接続します。

    この後、ロングワードを VRAM にアップロードできます。VRAM 自動インクリメント モードがあるため、CRAM とは異なり、各ロングワードにアドレスを指定する必要がありません。 VDP レジスタ フラグ 0x0F (dc.b $02) を使用して有効にできます。

      lea Tiles,a0 
      move.l #$40200000,vdp_control_port; write to VRAM command 
      move.w #6136,d0 ; (767 tiles * 8 rows) counter 
    TilesVRAMLoop: 
      move.l (a0)+,vdp_data_port; 
      dbra d0,TilesVRAMLoop 
    

    プレーン A/B のタイル インデックス

    次に、インデックスに従って画面をタイルで埋める必要があります。これを行うには、VDP レジスタ (0x02、0x04) に入力されるプレーン A/B アドレスで VRAM が埋められます。難しいアドレス指定の詳細については、Sega のマニュアルを参照してください。この例では、VRAM アドレスは 0xC000 です。そこにインデックスをアップロードしましょう。

    いずれにせよ、画像はオフスクリーン VRAM スペースを埋めるため、スクリーンスペースを描画した後、レンダラーは描画を停止し、カーソルが新しい行に移動したときに再び続行する必要があります。これを実装する方法には多くのオプションがあります。私は、画像幅カウンタとカーソル位置カウンタの 2 つのレジスタをカウントする最も単純なバージョンを使用しました。

    コード例:

      move.w #0,d0     ; column index 
      move.w #1,d1     ; tile index 
      move.l #$40000003,(vdp_control_port) ; initial drawing location 
      move.l #2500,d7     ; how many tiles to draw (entire screen ~2500) 
    
    imageWidth = 31 
    screenWidth = 64 
    
    FillBackgroundStep: 
      cmp.w	#imageWidth,d0 
      ble.w	FillBackgroundStepFill 
    FillBackgroundStep2: 
      cmp.w	#imageWidth,d0 
      bgt.w	FillBackgroundStepSkip 
    FillBackgroundStep3: 
      add #1,d0 
      cmp.w	#screenWidth,d0 
      bge.w	FillBackgroundStepNewRow 
    FillBackgroundStep4: 
      dbra d7,FillBackgroundStep    ; loop to next tile 
    
    Stuck: 
      nop 
      jmp Stuck 
    
    FillBackgroundStepNewRow: 
      move.w #0,d0 
      jmp FillBackgroundStep4 
    FillBackgroundStepFill: 
      move.w d1,(vdp_data_port)    ; copy the pattern to VPD 
      add #1,d1 
      jmp FillBackgroundStep2 
    FillBackgroundStepSkip: 
      move.w #0,(vdp_data_port)    ; copy the pattern to VPD 
      jmp FillBackgroundStep3 
    

    この後は、vasm を使用して rom を組み立て、シミュレーターを起動して画像を確認するだけです。

    デバッグ

    すべてがすぐにうまくいくわけではないため、次の Exodus エミュレータ ツールをお勧めします。

    <オル>

  • M68k プロセッサ デバッガ
  • m68k プロセッサ サイクル数の変更 (デバッガのスローモーション モードの場合)
  • ビューア CRAM、VRAM、プレーン A/B
  • m68k のドキュメントと使用されているオペコードを注意深く読んでください (すべてが一見したほど明白であるわけではありません)
  • Github でゲーム コード/逆アセンブリのサンプルを表示する
  • プロセッサ例外のサブルーチンを実装して処理する
  • プロセッサ例外サブルーチンへのポインタは、ROM ヘッダーに配置されます。GitHub には、genesis-debugger と呼ばれる、Sega 用の対話型ランタイム デバッガーを備えたプロジェクトもあります。

    利用可能なツールをすべて使用し、昔ながらの優れたコーディングを行い、Blast Processing を使用することもできます。

    リンク

    https://gitlab.com/demensdeum /segagenesisamples/-/tree/main/6Image/vasm
    http://devster.monkeeh.com/sega/imagenesis/
    https://github.com/flamewing/genesis-debugger

    ソース

    https://www.chibiakumas.com/68000/helloworld .php#レッスンH5
    https://huguesjohnson.com/programming/genesis/tiles-sprites/

     

    Sega Genesis #2 のアセンブリでの書き込み

    この投稿では、アセンブリ言語で将棋パレットに色を読み込む方法について説明します。
    Exodus エミュレータの最終結果は次のようになります。

    プロセスを簡単にするために、Genesis Software Manual (1989) という名前の PDF をインターネット上で見つけてください。このマニュアルにはプロセス全体が詳細に説明されています。実際、このメモはオリジナルのマニュアルの解説です。< /p>

    Sega エミュレータの VDP チップに色を書き込むには、次のことを行う必要があります。

    • TMSS 保護を無効にする
    • 正しいパラメータを VDP レジスタに書き込む
    • 希望の色を CRAM に書き込みます

    アセンブリには、vasmm68k_mot とお気に入りのテキスト エディタ (echo など) を使用します。アセンブリは次のコマンドで実行されます。

    Порты VDP

    VDP чип общается с M68K через два порта в оперативной памяти – порт контроля и порт данных.
    По сути:

    1. Через порт контроля можно выставлять значения регистрам VDP.
    2. Также порт контроля является указателем на ту часть VDP (VRAM, CRAM, VSRAM etc.) через которую передаются данные через порт данных

    Интересная информация: Сега сохранила совместимость с играми Master System, на что указывает MODE 4 из мануала разработчика, в нем VDP переключается в режим Master System.

    Объявим порты контроля и данных:

    vdp_data_port        = $C00000

    Отключить систему защиты TMSS

    Защита от нелицензионных игр TMSS имеет несколько вариантов разблокировки, например требуется чтобы до обращения к VDP в адресном регистре A1 лежала строка “SEGA”.

    MOVE.B A1,D0; Получаем версию хардвары цифрой из A1 в регистр D0 
    ANDI.B 0x0F,D0; По маске берем последние биты, чтобы ничего не сломать 
    BEQ.B SkipTmss; Если версия равна 0, скорее всего это японка или эмулятор без включенного TMSS, тогда идем в сабрутину SkipTmss 
    MOVE.L "SEGA",A1; Или записываем строку SEGA в A1 
    

    正しいパラメータを VDP レジスタに書き込みます

    そもそも、なぜ VDP レジスタに正しいパラメータを設定する必要があるのでしょうか? VDP は多くの機能を備えているため、レンダリングする前に必要な機能を使用して VDP を初期化する必要があるという考えです。そうしないと、VDP が VDP に何を求めているかを理解できなくなります。

    各レジスタは、特定の設定/動作モードを担当します。 Segov マニュアルには、24 の各レジスタのすべてのビット/フラグと、レジスタ自体の説明が記載されています。

    bigevilcorporation ブログからのコメントを含む既製のパラメーターを使用してみましょう。

    
    VDPReg0:   dc.b $14 ;  0: H interrupt on, palettes on 
    VDPReg1:   dc.b $74 ;  1: V interrupt on, display on, DMA on, Genesis mode on 
    VDPReg2:   dc.b $30 ;  2: Pattern table for Scroll Plane A at VRAM $C000 
                        ;     (bits 3-5 = bits 13-15) 
    VDPReg3:   dc.b $00 ;  3: Pattern table for Window Plane at VRAM $0000 
                        ;     (disabled) (bits 1-5 = bits 11-15) 
    VDPReg4:   dc.b $07 ;  4: Pattern table for Scroll Plane B at VRAM $E000 
                        ;     (bits 0-2 = bits 11-15) 
    VDPReg5:   dc.b $78 ;  5: Sprite table at VRAM $F000 (bits 0-6 = bits 9-15) 
    VDPReg6:   dc.b $00 ;  6: Unused 
    VDPReg7:   dc.b $00 ;  7: Background colour - bits 0-3 = colour, 
                        ;     bits 4-5 = palette 
    VDPReg8:   dc.b $00 ;  8: Unused 
    VDPReg9:   dc.b $00 ;  9: Unused 
    VDPRegA:   dc.b $FF ; 10: Frequency of Horiz. interrupt in Rasters 
                        ;     (number of lines travelled by the beam) 
    VDPRegB:   dc.b $00 ; 11: External interrupts off, V scroll fullscreen, 
                        ;     H scroll fullscreen 
    VDPRegC:   dc.b $81 ; 12: Shadows and highlights off, interlace off, 
                        ;     H40 mode (320 x 224 screen res) 
    VDPRegD:   dc.b $3F ; 13: Horiz. scroll table at VRAM $FC00 (bits 0-5) 
    VDPRegE:   dc.b $00 ; 14: Unused 
    VDPRegF:   dc.b $02 ; 15: Autoincrement 2 bytes 
    VDPReg10:  dc.b $01 ; 16: Vert. scroll 32, Horiz. scroll 64 
    VDPReg11:  dc.b $00 ; 17: Window Plane X pos 0 left 
                        ;     (pos in bits 0-4, left/right in bit 7) 
    VDPReg12:  dc.b $00 ; 18: Window Plane Y pos 0 up 
                        ;     (pos in bits 0-4, up/down in bit 7) 
    VDPReg13:  dc.b $FF ; 19: DMA length lo byte 
    VDPReg14:  dc.b $FF ; 20: DMA length hi byte 
    VDPReg15:  dc.b $00 ; 21: DMA source address lo byte 
    VDPReg16:  dc.b $00 ; 22: DMA source address mid byte 
    VDPReg17:  dc.b $80 ; 23: DMA source address hi byte, 
                        ;     memory-to-VRAM mode (bits 6-7)  
    

    OK、制御ポートに移動して、すべてのフラグを VDP レジスタに書き込みましょう。

        move.l  #VDPRegisters,a0 ; Пишем адрес таблицы параметров в A1 
        move.l  #$18,d0          ; Счетчик цикла - 24 = 18 (HEX) в D0 
        move.l  #$00008000,d1    ; Готовим команду на запись в регистр VDP по индексу 0, по мануалу - 1000 0000 0000 0000 (BIN) = 8000 (HEX) 
    
    FillInitialStateForVDPRegistersLoop: 
        move.b  (a0)+,d1         ; Записываем в D1 итоговое значение регистра VDP из таблицы параметров, на отправку в порт контроля VDP  
        move.w  d1,vdp_control_port     ; Отправляем итоговую команду + значение из D1 в порт контроля VDP 
        add.w   #$0100,d1        ; Поднимаем индекс регистра VDP на 1 (бинарное сложение +1 к индексу по мануалу Сеги) 
        dbra    d0,FillInitialStateForVDPRegistersLoop ; Уменьшаем счетчик регистров, продолжаем цикл если необходимо

    Самое сложное это прочитать мануал и понять в каком формате подаются данные на порт контроля, опытные разработчики разберутся сразу, а вот неопытные… Немного подумают и поймут, что синтаксис для записи регистров такой:

    0B100(5 бит – индекс регистра)(8 бит/байт – значение)

    0B1000001001000101 – записать в регистр VDP 2 (00010), значение флажков 01000101.

    Записать нужные цвета в CRAM

    Далее идем писать два цвета в память цветов CRAM (Color RAM). Для этого пишем в порт контроля команду на доступ к цвету по индексу 0 в CRAM и отправляем по дата порту цвет. Все!

    Пример:

        move.l  #$C0000000,vdp_control_port ; Доступ к цвету по индексу 0 в CRAM через порт контроля  
        move.w  #228,d0; Цвет в D0 
        move.w  d0,vdp_data_port; Отправляем цвет в порт данных 
    

    Exodus でエミュレータを構築して実行すると、画面がカラー 228 で塗りつぶされるはずです。

    最後のバイト 127 に基づいて、2 番目の色で塗りつぶしてみましょう。

    <コード>

      move.l  #$C07f0000,vdp_control_port ; Доступ к цвету по байту 127 в CRAM через порт контроля 
      move.w  #69,d0; Цвет в D0 
      move.w  d0,vdp_data_port; Отправляем цвет в порт данных 
    

    リンク

    https://gitlab.com/demensdeum/segagenesissamples
    https://www.exodusemulator.com/
    http://sun.hasenbraten.de/vasm/
    https://tomeko.net/online_tools/bin_to_32bit_hex.php?lang=en

    ソース

    https://namelessalgorithm.com/genesis/blog/genesis/https://plutiedev.com/vdp-commands
    https://huguesjohnson.com/programming/genesis/palettes/
    https://www.chibiakumas.com/68000/helloworld.php#LessonH5
    https://blog.bigevilcorporation.co.uk/2012/03/09/sega-megadrive-3-awaking-the-beast/

    Sega Genesis のアセンブリでの書き込み #1

    Motorola 68000 Assembly での古典的な Sega Genesis コンソール用のゲームの作成に特化した最初の記事。

    Sega 用の最も単純な無限ループを書いてみましょう。このためには、アセンブラ、逆アセンブラを備えたエミュレータ、お気に入りのテキスト エディタ、Sega rum の構造の基本的な理解が必要です。

    開発には、独自のアセンブラ/逆アセンブラ Gen68KryBaby を使用します。

    https://gitlab.com/demensdeum/gen68krybaby/

    このツールは Python 3 で開発されており、アセンブリの場合、拡張子 .asm または .gen68KryBabyDisasm を持つファイルが入力として提供され、出力は拡張子 .gen68KryBabyAsm.bin を持つファイルとなり、エミュレータまたは上で実行できます。本物のコンソール (コンソールが爆発する可能性があるので、注意して離れてください!)

    ROM の逆アセンブルもサポートされており、そのためには、.asm または .gen68KryBabyDisasm 拡張子を付けずに、ROM ファイルを入力として送信する必要があります。オペコードのサポートは、トピックに対する私の関心と貢献者の参加に応じて増減します。

    構造

    Sega rom ヘッダーは最初の 512 バイトを占めます。これには、ゲーム、名前、サポートされている周辺機器、チェックサム、およびその他のシステム フラグに関する情報が含まれています。タイトルがなければ、コンソールはラム酒を確認することさえせず、それが間違っていると判断し、「ここで何をくれるのですか?」と言うと思います。

    ヘッダーの後にサブルーチン/リセット サブルーチンが続き、ここで m68K プロセッサが作業を開始します。さて、それは小さな問題です –オペコード (オペレーション コード) を見つけます。つまり、何もせず (!)、メモリ内のアドレスのサブルーチンに切り替えます。グーグルで検索すると、何もしない NOP オペコードと、引数アドレスへの無条件ジャンプを実行する JSR オペコードを見つけることができます。つまり、何の気まぐれもせずに、要求した場所にキャリッジを移動するだけです。

    すべてをまとめる

    ROM のヘッダードナーはベータ版のゲームの 1 つであり、現在は 16 進データとして記録されています。

    
     00 ff 2b 52 00 00 02 00 00 00 49 90 00 00 49 90 00 00 49 90 00...и т.д. 

    Код программы со-но представляет из себя объявление сабрутины Reset/EntryPoint в 512 (0x200) байте, NOP, возврат каретки к 0x00000200, таким образом мы получим бесконечный цикл.

    Ассемблерный код сабрутины Reset/EntryPoint:

        NOP
        NOP
        NOP 
        NOP
        NOP
        JSR 0x00000200  
    

    ROM ヘッダーを含む完全な例:

    https://gitlab.com /demensdeum/segagenesisamples/-/blob/main/1InfiniteLoop/1infiniteloop.asm

    次に収集します:

    Запускаем ром 1infiniteloop.asm.gen68KryBabyAsm.bin в режиме дебаггера эмулятора Exodus/Gens, смотрим что m68K корректно считывает NOP, и бесконечно прыгает к EntryPoint в 0x200 на JSR

    Здесь должен быть Соник показывающий V, но он уехал на Вакен.

    Ссылки

    https://gitlab.com/demensdeum/gen68krybaby/

    https://gitlab.com/demensdeum/segagenesissamples

    https://www.exodusemulator.com/downloads/release-archive

    Источники

    ROM Hacking Demo – Genesis and SNES games in 480i

    http://68k.hax.com/

    https://www.chibiakumas.com/68000/genesis.php

    https://plutiedev.com/rom-header

    https://blog.bigevilcorporation.co.uk/2012/02/28/sega-megadrive-1-getting-started/

    https://opensource.apple.com/source/cctools/cctools-836/as/m68k-opcode.h.auto.html

    フレームスチールエンジンランナー

    フレーム スチール エンジン ランナーをご紹介します – Flame Steel Engine ツールキットに基づいてマルチメディア アプリケーションを起動するためのプラットフォーム。サポートされているプラ​​ットフォームは、Windows、MacOS、Linux、Android、iOS、HTML 5 です。アプリケーション コード開発の重点は、スクリプト作成に移ってきています。現時点では TinyJS を使用して JavaScript のサポートが追加されており、ツールキット自体とエンジンは引き続きハードウェアに近い言語 (C、C++、Rust など) で開発されます
    Flame Steel Engine Runner Demo
    以下のページでは、キューブを回転させ、JavaScript でコードを記述し、[ファイルのアップロード] ボタンを使用してモデル、サウンド、音楽、コードをアップロードし、[実行] ボタンを使用して main.js ファイルから開始することができます。
    https://demensdeum.com/demos/FlameSteelEngineRunner/

    クレイモーメント –スクリプトファイルをマージするためのユーティリティ

    スクリプト ファイルをマージするためのユーティリティを紹介します – KleyMoment、これもファイルを元に戻すためのリバース ユーティリティです。このユーティリティを使用すると、JavaScript ファイルを 1 つにマージできます。
    このツールは Python 3 で実装されており、次のようなシンプルなコマンド ライン インターフェイスを備えています。

    <前>python3 KleyMoment.py ファイル拡張子ディレクトリContainingFiles出力ファイル

    たとえば、script ディレクトリの js ファイルを output.js ファイルに再帰的にマージします。

    <前>python3 KleyMoment.py jsスクリプトoutput.js

    また、ファイルを貼り付けて戻すためのユーティリティである AntiKleyMoment は、接着されたファイルを入力として受け取ります。例:

    <前>python3 AntiKleyMoment.py 出力.js

    リポジトリ:
    https://gitlab.com/demensdeum/kleymoment/

    スペースジャガー アクションRPG 0.0.4

    Webassembly 用の Space Jaguar アクション RPG ゲームの最初のプロトタイプ:

    読み込み表示がなければ、読み込みに時間がかかります (53MB)。

    スペース ジャガー アクション RPG –宇宙海賊のライフシミュレーター。現時点では、最も単純なゲーム メカニクスが利用可能です。

    • 宇宙を飛ぶ
    • 死ぬ
    • 食べる
    • 寝てください
    • チームを雇う
    • 落ち着きのない、急速に流れる時間の流れを見てください

    Web バージョンでは 3D シーンのレンダリングの最適化が不十分なため、3D 環境内を歩き回る機能は利用できません。最適化は将来のバージョンで追加される予定です。

    エンジン、ゲーム、スクリプトのソース コードは、MIT ライセンスに基づいて入手できます。改善のためのすべての提案は非常に好意的に受け取られています。

    https://gitlab.com/demensdeum/space-jaguar -アクション RPG

    Linux のネイティブ バージョンのスクリーンショット:

    ポールに立っていた男や驚くべき創意工夫についての話をどれほど見逃していたか

    このノートでは、アプリケーションの開発、サポート、およびチーム開発環境におけるアーキテクチャ上の決定の重要性について書きます。

    セルフ手術用ナプキンのルシファー・ゴルゴンゾーラ教授。ルーブ・ゴールドバーグ

    若い頃、私はタクシー注文アプリケーションの開発に取り組んでいました。プログラムでは、乗車場所と降車場所を選択し、旅行費用と料金の種類を計算し、実際にタクシーを注文することができます。リリース前の最終段階でアプリケーションを受け取り、いくつかの修正を加えた後、アプリケーションは AppStore でリリースされました。すでにその段階で、チーム全体は、実装が非常に不十分で、デザインパターンが使用されておらず、システムのすべてのコンポーネントが緊密に接続されており、一般に、それを1つの大きな連続クラス(神オブジェクト)に書き込むことが可能であることを理解していました。何も変わらなかっただろうから、階級が責任の境界をどのように混同し、その総量においてデッドカップリングで互いに重なり合ったかということだ。その後、経営陣は正しいアーキテクチャを使用してアプリケーションを最初から作成することを決定し、それが実行され、最終製品は数十の B2B クライアントに実装されました。

    しかし、私は過去の建築に関する奇妙な出来事について説明します。その出来事から、私は時々夜中に冷や汗をかきながら目が覚めたり、昼間に突然思い出してヒステリックに笑い始めたりすることがあります。問題は、最初にポールにいる男を攻撃できなかったということで、これによりアプリケーションの大部分がダウンしましたが、まず最初に。

    その日は通常の営業日で、顧客の 1 人がアプリケーションのデザインを少し改良するというタスクを受けました –受け取り住所の選択画面の中央にあるアイコンを数ピクセル上に移動するのは簡単です。さて、専門的にタスクを 10 分で見積もったので、何も疑うことなくアイコンを 20 ピクセル上に上げ、タクシーの注文を確認することにしました。

    何?アプリに注文ボタンが表示されなくなりました?どうしてこんなことになったのでしょうか?

    自分の目を信じられませんでした。アイコンを 20 ピクセル上げた後、アプリケーションに注文を続けるボタンが表示されなくなりました。変更をロールバックすると、ボタンが再び表示されました。ここで何かが間違っていました。デバッガで 20 分を費やした後、重複するクラスへの呼び出しのスパゲッティを解くのに少しうんざりしましたが、*画像を移動するとアプリケーションのロジックが実際に変わる*ことがわかりました。

    すべては中央のアイコンに関するものでした –ポールの上にいる男性が、カードを動かすときに飛び上がってカメラの動きをアニメーション化しました。このアニメーションの後に、下部のボタンが消えました。どうやらプログラムは、20 ピクセル移動した男性がジャンプ中であると判断したため、内部ロジックに従って確認ボタンを非表示にしました。

    どうしてこのようなことが起こるのでしょうか?画面の *状態* は本当にステート マシンのパターンではなく、ポール上の男の位置の *表現* に依存するのでしょうか?

    その結果、マップが描画されるたびにアプリケーション *画面の中央を視覚的に突いて*、そこに何があるかを確認します。ポールの上に人がいる場合は、マップ シフト アニメーションが終了したことを意味し、表示する必要があります。ボタン。男性がそこにいない場合は、マップが移動し、ボタンが非表示になる必要があります。

    上記の例では、すべて問題ありません。第一に、これはゴールドバーグ マシン (難解なマシン) の例であり、第二に、開発者がチーム内の他の開発者と何らかの形で対話することに消極的であることの例です (何も考えずに理解してみてください)私)、第三に、SOLID、パターン(コード臭)、MVC 違反などに基づいてすべての問題をリストできます。

    このようなことをしないようにし、あらゆる方向に発展し、同僚の仕事を助けてください。皆さん明けましておめでとうございます)

    リンク

    https://ru.wikipedia.org/wiki/Goldberg_Machine

    https://ru.wikipedia.org/wiki/SOLID

    https://refactoring.guru/ru/refactoring/smells

    https://ru.wikipedia.org/wiki/Model -View-Controller

    https://refactoring.guru/ru/design-patterns/state

    グループを推測してください

    この投稿では、fasttext テキスト分類子の操作について説明します。

    ファーストテキスト –テキスト分類のための機械学習ライブラリ。彼女に曲のタイトルでメタルバンドを見分けるように教えてみましょう。これを行うために、データセットを使用した教師あり学習を使用します。

    グループ名を付けた曲のデータセットを作成しましょう:

    __label__metallica fuel
    __label__metallica escape
    __label__black_sabbath gypsy
    __label__black_sabbath snowblind
    __label__black_sabbath am i going insane
    __label__anthrax anthrax
    __label__anthrax i'm alive
    __label__anthrax antisocial
    [и т.д.]

    Формат обучающей выборки:

    Обучим fasttext и сохраним модель:

    model.save_model("model.bin")
    

    トレーニングされたモデルをロードし、曲の名前でグループを識別するように要求します。

    predictResult = model.predict("Bleed")
    print(predictResult)

    В результате мы получим список классов на которые похож данный пример, с указанием уровня похожести цифрой, в нашем случае похожесть названия песни Bleed на одну из групп датасета.
    Для того чтобы модель fasttext умела работать с датасетом выходящим за границы обучающей выборки, используют режим autotune с использованием файла валидации (файл тест). Во время автотюна fasttext подбирает оптимальные гиперпараметры модели, проводя валидацию результата на выборке из тест файла. Время автотюна ограничивается пользователем в самостоятельно, с помощью передачи аргумента autotuneDuration.
    Пример создания модели с использованием файла тест: