[#31] Re: Wyzwania programistyczne

@peceha, post #30

~1 KB?


1244 bajty konkretnie. Bez stringu z wersja ($VER:) zajmuje 1212 bajty. Moze daloby sie jeszcze skrocic ale co tam :) Pewnie w assemblerze daloby sie cos jeszcze ugrac :)

O, prosze. Zamiana OpenWindowTags na porzadne OpenWindowTagList i osobno zadeklarowana strukture TagItem jeszcze troszke kod skrocila. Teraz plik wykonywalny bez $VER ma 1152 bajty. Gdyby wywalic z kodu startowego obsluge WBMessage jeszcze by sie skrocil ale za to wywalalby sie przy starcie z workbencha...
[#32] Re: Wyzwania programistyczne

@mschulz, post #12


A nie bylo pythona na os4/morphos-a?


Przerobiłem trochę program mschulza aby zadziałał na Blenderze. Co prawda pod OS4 jest jakiś zabytkowy Blender, ale pod MorphOS gdzie jest trochę nowszy powinno ruszyć. Niestety nie mam jak sprawdzić.


[#33] Re: Wyzwania programistyczne

@peceha, post #30

Nie wiem, nie kompilowałem. Jak było widać odpalane z interpretera, ale z ciekawości sprawdzę...

[Kilka minut później...]

9928 bajtów bez biblioteki AMOSa... Z biblioteką należy doliczyć jakieś 65kb albo lepiej, ale niestety takie uroki AMOSa
[#34] Re: Wyzwania programistyczne

@Mandi, post #33

To w sumie calkiem niewiele.
[#35] Re: Wyzwania programistyczne

@mschulz, post #31

No więc też wstawiłem Openwindowtaglist() i co za tym poszło musiałem zrobić systemowa obsługę "eventów".
Te dwie zmiany zmniejszyły binarke o 10kB. Teraz ma 13 no ale to dalej więcej niż w amosie :)
[#36] Re: Wyzwania programistyczne

@peceha, post #35

Ale ta binarka bez biblioteki AMOSa się nie wykona... Uroki AMOSa
[#37] Re: Wyzwania programistyczne

@mschulz, post #21

ASM nie kumam ni w ząb.
Wasze generatory liczb pseudolosowych, skąd pobierają "element peudolosowy"? Np z pozycji liczby w zegarze systemowym?
[#38] Re: Wyzwania programistyczne

@jubi, post #32

Czy morphos dziala na jakiejs amidze ?
[#39] Re: Wyzwania programistyczne

@Goor, post #38

tak

Ostatnia aktualizacja: 25.09.2018 20:38:44 przez BULI
[#40] Re: Wyzwania programistyczne

@BULI, post #37

Wasze generatory liczb pseudolosowych, skąd pobierają "element peudolosowy"?
W zasadzie tylko kod w poście #20 bierze entropię skądś, pozostałe zakładają, że ona już jest w wartości początkowej (seed) i nie obchodzi ich jak ją zorganizowaliśmy. Adres $DFF006 to rejestr VHPOSR, zawierajacy w starszym bajcie 8 młodszych bitów numeru aktualnie wyświetlanej linii obrazu, w młodszym bajcie poziomą pozycję w tej linii w pikselach podzieloną przez 2. Zawartość tego rejestru zmienia się co 280 ns. Adres $BFE801 należy do układu CIA A i jest to najmłodszy bajt licznika zliczającego impulsy synchronizacji pionowej obrazu. Adres $BFD800 z kolei to najmłodszy bajt licznika zliczającego impulsy synchronizacji poziomej.

W systemowych programach można też się posłużyć młodszymi bitami wartości czasu systemowego zwracanego przez funkcję ReadEClock() z timer.device. Ta wartość jest zwiększana o 1 w takt sygnału E, o częstotliwości 709 kHz (w Amigach z systemem PAL, dla NTSC jest to 715 kHz).
[#41] Re: Wyzwania programistyczne

@Krashan, post #19

Odnośnie asemblera, to w grze Benefactor jest używany fajny generator liczb pseudolosowych. W tej grze jest kilka elementów losowych jak skacząca żabka w Merry Winterland, czy wybuchające purchawki w Underworld.

Dune 2 też ma swój generator, choć tutaj raczej pochodzi z biblioteki standardowej C.

Ot, takie ciekawostki.
[#42] Re: Wyzwania programistyczne

@BULI, post #39

Na jakiej ?
[#43] Re: Wyzwania programistyczne

@Krashan, post #40

Dzięki za wyjaśnienie OK

Swego czasu musiałem sobie taki "bardzo prosty generator" stworzyć do praktycznego pokazania szyfrowania z użyciem klucza publicznego. Skorzystałem z funkcji sczytywania czasu systemowego + jakieś wariacje na liczbach ok, racja

@Hexmage960

Dobrze wiedzieć OK
[#44] Re: Wyzwania programistyczne

@Goor, post #42

Tu masz troche informacji:
https://pl.wikipedia.org/wiki/MorphOS
Ewntualnie polecam:
www.morphos-team.net
Najlepsza informacja. U samego zrodla.
[#45] Re: Wyzwania programistyczne

@Hexmage960, post #41

Podobno (nie sprawdzałem osobiście) śmieszny generator liczb pseudolosowych jest w Quake 1. Jako liczbę losową z przedziału od 0.0 do 1.0 brana jest któraś składowa (X, Y, Z) normalnej 3D. Z definicji normalna ma długość 1.0 więc jej składowa zawsze jest z przedziału -1.0 do 1.0. Ale na jakiej zasadzie jest wybierana ta normalna i jej składowa to już mi nie powiedział ten człowiek, który mi tę ciekawostkę opowiedział z 15 lat temu.
[#46] Re: Wyzwania programistyczne

@Hexmage960, post #41

Nie zawsze zależy nam na tym żeby liczba pseudolosowa była losowa. Czasem wręcz chcemy żeby te liczby były w 100% przewidywalne i ciągi się powtarzały. Na tym bazują podobno współrzędne wszystkich planet w kosmosie gry "Elite" i "Frontier Elite II". Stosuje się tutaj generator liczb pseudolosowych z konkretnym random seed. Sam też tego użyłem 15 lat temu do generowania powtarzalnych dróg w grze na telefony (w Java2ME), pt. "Road Chase". Sprawdziło się bardzo fajnie. Tysiące zakrętów na różnych trasach zajęły wręcz zero bajtów pamięci, a były w pełni powtarzalne.
[#47] Re: Wyzwania programistyczne

@MDW, post #46

Wszystko zależy jaki rozkład mają mieć liczby "losowe" i jaki zakres. Bo jeśli mamy ulong seed = 0; to nawet coś takiego jest liczbą losową o zakresie 2^32.
add.l #1,seed ;
[#48] Re: Wyzwania programistyczne

@jubi, post #32

Z ciekawości spróbowałem odpalić ten skrypt na Blenderze 2.63 i niestety wywalił błąd przy drugiej linijce (import bmesh). Pech.
[#49] Re: Wyzwania programistyczne

@recedent, post #48

Widocznie BMesh jest niedostępny w tej wersji, ale można użyć starszej metody. Przerobiłem tak, aby nie korzystało z BMesh, wrzuć screenshota z MorphOS jak się uda.
import bpy
import math
import random

def create_mesh(verts):
    mesh = bpy.data.meshes.new("mesh")
    mesh.from_pydata(verts, [], [])
    mesh.update()
    return mesh

def link_object(object):
    scene = bpy.context.scene
    scene.objects.link(object)

def create_object(object_name, verts):
    object = bpy.data.objects.new(object_name, create_mesh(verts))
    link_object(object)
    object.select = True

verts = []
scale_factor = 100

# Rozmiar planszy
(width, height) = (800, 800)

# Wysokosc trojkata
triangle_h = 600
triangle_a = int(triangle_h * 2 / math.sqrt(3))

# Koordynaty trojkata - zawsze na srodku okna
triangle = ((width/2, height/2-triangle_h/2), 
            (width/2 - triangle_a / 2, height / 2+triangle_h/2), 
            (width/2 + triangle_a / 2, height/2+triangle_h/2))

# Wylosuj pierwszy punkt
x = (random.randint(0, width-1), random.randint(0, height-1))

cnt = 0
running = True

# Glowna petla
while running:
  cnt = cnt + 1
  
  # Rysuj punkt
  verts.append((x[0]/scale_factor, x[1]/scale_factor, 0))
  
  # Wylosuj jeden z punktow trojkata
  tx = triangle[random.randint(0, 2)]

  # Oblicz nowe koordynaty
  x = ((x[0] + tx[0]) / 2, (x[1] + tx[1]) / 2)

  if cnt % 100000 == 0:
    running = False

create_object("Trojkat Sierpinskiego", verts)
[#50] Re: Wyzwania programistyczne

@MDW, post #46

Z tą przewidywalnością w przypadku liczb pseudo-losowych mam do czynienie praktycznie zawsze, tylko kwestia tego o czym wspomniał @asman
Wszystko zależy jaki rozkład mają mieć liczby "losowe" i jaki zakres.

Tak, że rzeczywiście, można "tym sterować".

Pamiętam, że pierwsza wersja mojego generator, tzw "ambitna", potrafiła zatkać Pentium3 800mhz na jakieś parę minut.. Wersja finalna, do prezentacji została zdegradowana tak, żeby wszystko nie trwało dłużej niż kilkadziesiąt sekund ok, racja
[#51] Re: Wyzwania programistyczne

@jubi, post #49

Ta wersja zadziałała:

[#52] Re: Wyzwania programistyczne

@teh_KaiN, post #6

Wersja w E:

MODULE 	'intuition/intuition',
		'intuition/screens',
		'other/random'

ENUM 	ERR_SCREEN, ERR_WINDOW;
		
DEF wierzcholek, th, ta
DEF win_width, win_height, win_pos_x, win_pos_y;
  
DEF mywnd	= NIL:PTR TO window, myscr	= NIL:PTR TO screen, msg

OBJECT punkt
        x:INT
        y:INT
ENDOBJECT 

DEF punkty[3]:ARRAY OF punkt
DEF pStart:punkt

PROC main() HANDLE

	WriteF('Trojkat Sierpinskiego\n')
	seedRand()
	
	myscr:=LockPubScreen(NIL)
	IF myscr = NIL THEN Raise( ERR_SCREEN )
		
	win_width:=		myscr.width
	win_height:=	myscr.height - myscr.barheight - 1
	win_pos_y:=		myscr.barheight + 1
	win_pos_x:=		0
	
	UnlockPubScreen(NIL,myscr)

	th:= win_height-4
	ta:= ! 2.0 * th / 1.7320508
	
	punkty[0].x := win_width/2
	punkty[0].y := (win_height-th)/2
	
	punkty[1].x := (win_width-ta)/2+ta
	punkty[1].y := (win_height-th)/2+th
	
	punkty[2].x := (win_width-ta)/2
	punkty[2].y := (win_height-th)/2+th
	
	mywnd:=OpenW (	win_pos_x, win_pos_y, win_width, win_height ,
			IDCMP_RAWKEY, WFLG_ACTIVATE OR WFLG_BORDERLESS,
			NIL, NIL, 1, NIL)
			
	IF mywnd = NIL THEN Raise( ERR_WINDOW )
	
	pStart.x := getRandRange(win_width-1)
	pStart.y := getRandRange(win_height-1)
	
	REPEAT
		/* 	jedynym mozliwym messagem jest IDCMP_RAWKEY,
			wiec liczymy do pierwszego message'a 			*/
		msg:=GetMsg (mywnd.userport)
		
		wierzcholek := getRandRange(3)
		
		pStart.x := (pStart.x+punkty[wierzcholek].x)/2 
		pStart.y := (pStart.y+punkty[wierzcholek].y)/2
		
		Plot(pStart.x,pStart.y, 1)
	
	UNTIL msg <> NIL
	
	CloseW( mywnd )
	
  EXCEPT

	SELECT exception
		CASE ERR_SCREEN ; WriteF ('Error LockPubScreen()\n')
		CASE ERR_WINDOW ; WriteF ('Error OpenW()\n')
	ENDSELECT

    IF mywnd	<> NIL THEN CloseW(mywnd)
    IF myscr	<> NIL THEN UnlockPubScreen(NIL,myscr)
	
ENDPROC


Binarka z powyższego 2488 bajtów, jak najbardziej do optymalizacji, a i obsługa wyjątków pewnie niepotrzebna w takim zadaniu.



Ostatnia aktualizacja: 26.09.2018 22:19:54 przez vojo
[#53] Re: Wyzwania programistyczne

@vojo, post #52

Przyznam że podoba mi się takie porównanie w kilku językach.
Udało mi się zmniejszyć plik wykonywalny w BB do 5KB. Myślę że jeszcze powinienem dać radę trochę uszczypnąć.
[#54] Re: Wyzwania programistyczne

@peceha, post #53

To teraz przydałby się mały benchmark wykonany na "klasyku": porównanie czasu wykonania w różnych językach.
[#55] Re: Wyzwania programistyczne

@peceha, post #53

Udało mi się zmniejszyć plik wykonywalny w BB do 5KB. Myślę że jeszcze powinienem dać radę trochę uszczypnąć

a napiszesz coś więcej w jaki sposób optymalizujesz wyjściową binarkę?
[#56] Re: Wyzwania programistyczne

@retronav, post #55

Będę w domu po 21 to wtedy wkleje cały kod.
Na aminecie znalazłem kiedyś text typu "tips and tricks" jak zmniejszyć binarke w BB.
Właściwie wszystko to sprowadzalo się do podmiany funkcji blitza tymi systemowymi.
W tej chwili mam tam systemowe otwieranie okna, szukanie ekranu wb.
Myślę że gdy zamienię "wbstartup" na to co wypatrzylem w kodzie mschulza to jeszcze uda mi się zmniejszyć całość
[#57] Re: Wyzwania programistyczne

@peceha, post #56

Dobra panowie. Troche sie z kodem jeszcze pobawilem:

1. Zamienilem zmienna globalna SysBase na *(struct ExecBase **)4UL. Jest lepiej ale nieoptymalnie bo kompilator rozbija to na 2 instrukcje zamiast jednej.
2. Kod startowy zwraca wynik funkcji main do systemu
3. Generator liczb pseudolosowych zwraca typ UWORD zamiast ULONG
4. Struktura Punkt ma koordynaty typu LONG (Takiego oczekuje graphics.library w swoich funkcjach) zamiast UWORD
5. IntuiMessage->Class jest sprawdzane bezposrednio, dopiero potem jest ReplyMsg
6. Modulo zastapilem mnozeniem. Poniewaz mozna zalozyc ze liczby losowe pojawiaja sie z rownym prawdopodobienstwem mozna zamiast:

UWORD x = rand() % WINDOW_WIDTH;


napisac

UWORD x = (rand() * WINDOW_WIDTH) >> 16;


Dla takiej samej wartosci startowej seed rozklad bedzie inny ale powinien byc rownie prawdopodobny.

Po tych zmianach plik wynikowy sie skurczyl do 996 bajtow (nadal mozna uruchomic z workbencha). Do pobrania tutaj: link

Screenshot z programu:


Kod dalej wymaga 68020 z powodu generatora liczb losowych. Kompilowane za pomoca gcc od bebbo:
m68k-amigaos-gcc -noixemul -Os -m68020 -nostartfiles -fno-inline -o dywan main.c


Rozmiar binarki moze sie jeszcze zmniejszyc/zwiekszyc w zaleznosci od rozmiar otwartego okna. Jak rozmiar bedzie potega dwojki to binarka sie jeszcze skurczy do 964 bajtow...
Mozna by optymalizowac dalej ale juz mi sie nie chce :)

Kod:
#include <exec/types.h>
#include <exec/execbase.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
#include <workbench/startup.h>
#include <graphics/gfxbase.h>
#include <graphics/gfx.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <utility/tagitem.h>

#undef SysBase
#define SysBase (*(struct ExecBase **)4)

int main(void);

/* Minimalny kod startowy */
int _start(void)
{
    struct Process *p = NULL;
    struct WBStartup *wbmsg = NULL;
    int ret;

    p = (struct Process *)SysBase->ThisTask;

    if (p->pr_CLI == 0)
    {
        WaitPort(&p->pr_MsgPort);
        wbmsg = (struct WBStartup *)GetMsg(&p->pr_MsgPort);
    }

    ret = main();

    if (wbmsg)
    {
        Forbid();
        ReplyMsg((struct Message *)wbmsg);
    }

    return ret;
}

#define WINDOW_WIDTH    400
#define WINDOW_HEIGHT   400
#define TRIANGLE_H      (WINDOW_HEIGHT * 4 / 5)
#define TRIANGLE_A      (TRIANGLE_H * 2 * 1000 / 1732)

ULONG seed = 1;

UWORD rand(void)
{
    seed = ((seed * 1103515245) + 12345) & 0x7fffffff;
    return seed;
}

struct Point {
    LONG x;
    LONG y;
};

struct Point points[3] = {
    { WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2 - TRIANGLE_H / 2 },
    { WINDOW_WIDTH / 2 - TRIANGLE_A / 2, WINDOW_HEIGHT / 2 + TRIANGLE_H / 2 },
    { WINDOW_WIDTH / 2 + TRIANGLE_A / 2, WINDOW_HEIGHT / 2 + TRIANGLE_H / 2 }
};

struct TagItem myWindowTags[] = {
    { WA_InnerWidth, WINDOW_WIDTH },
    { WA_InnerHeight, WINDOW_HEIGHT },
    { WA_DepthGadget, TRUE },
    { WA_CloseGadget, TRUE },
    { WA_DragBar, TRUE },
    { WA_Title, (Tag) "Dywan" },
    { WA_Activate, TRUE },
    { WA_GimmeZeroZero, TRUE },
    { WA_IDCMP, IDCMP_MOUSEBUTTONS | IDCMP_CLOSEWINDOW },
    { TAG_DONE, 0UL }
};

int main(void)
{
    struct IntuitionBase * IntuitionBase = NULL;
    struct GfxBase * GfxBase = NULL;

    IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 0);
    
    if (IntuitionBase != NULL)
    {
        GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0);

        if (GfxBase != NULL)
        {
            struct Window *myWindow = OpenWindowTagList(0, myWindowTags);
            
            if (myWindow != NULL)
            {
                struct RastPort *rastPort = myWindow->RPort;
                BYTE running = TRUE;
                SetAPen(rastPort, 1);

                /* Wylosuj pierwszy punkt */
                ULONG x = (rand() * WINDOW_WIDTH) >> 16;
                ULONG y = (rand() * WINDOW_HEIGHT) >> 16;

                Move(rastPort, points[0].x, points[0].y);
                Draw(rastPort, points[1].x, points[1].y);
                Draw(rastPort, points[2].x, points[2].y);
                Draw(rastPort, points[0].x, points[0].y);

                do {
                    struct IntuiMessage *msg;

                    while ((msg = (struct IntuiMessage *)GetMsg(myWindow->UserPort)) != 0)
                    {
                        if (msg->Class == IDCMP_CLOSEWINDOW)
                        {
                            running = FALSE;
                        }

                        ReplyMsg((struct Message *)msg);
                    }

                    /* Wylosuj punkt trojkata */
                    UWORD p = (rand() * 3) >> 16;

                    x = (x + points[p].x) / 2;
                    y = (y + points[p].y) / 2;

                    WritePixel(rastPort, x, y);

                } while(running == TRUE);

                CloseWindow(myWindow);
            }

            CloseLibrary((struct Library *)GfxBase);
        }

        CloseLibrary((struct Library *)IntuitionBase);
    }

    return 0;
}


Ostatnia aktualizacja: 27.09.2018 09:24:14 przez mschulz
[#58] Re: Wyzwania programistyczne

@mschulz, post #57

Przepraszam najmocniej. Zapomnialem usunac symbole (opcja -s dla kompilatora) i przez to binarka byla zbyt wielka. Teraz ma juz 728 bajtow (mialaby 696 bajtow dla okna o wymiarach 512x512). Zaktualizowana binarka ciagle pod tym samym linkiem :)
[#59] Re: Wyzwania programistyczne

@mschulz, post #58

Nie mam pytań ;)
[#60] Re: Wyzwania programistyczne

@mschulz, post #57

Bardzo fajna lekcja. Mam jeszcze sugestię aby polepszyć czytelność kodu:

if (IntuitionBase != NULL)
    {
        GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0);

        if (GfxBase != NULL)
        {
            struct Window *myWindow = OpenWindowTagList(0, myWindowTags);
            
            if (myWindow != NULL)
            {


Zamiast narastających poziomów zagłębień, można zastosować regułę "return early":

https://arne-mertz.de/2016/12/early-return/

Returning early, in this case, has several benefits: Obviously, there is no deep nesting like in the first version. This is enabled because we return early in case of errors: with a return (or throw) in the if block we do not need an else block for the rest of the function. In addition, handling the errors immediately enables us to push them out of our minds. We need not keep track of them to write that later else statement. Last but not least, the actual work is set at the end of the function and not hidden in the middle of that forest of if-else blocks. Arguably, that actual work should be factored out into its own function.
Na stronie www.PPA.pl, podobnie jak na wielu innych stronach internetowych, wykorzystywane są tzw. cookies (ciasteczka). Służą ona m.in. do tego, aby zalogować się na swoje konto, czy brać udział w ankietach. Ze względu na nowe regulacje prawne jesteśmy zobowiązani do poinformowania Cię o tym w wyraźniejszy niż dotychczas sposób. Dalsze korzystanie z naszej strony bez zmiany ustawień przeglądarki internetowej będzie oznaczać, że zgadzasz się na ich wykorzystywanie.
OK, rozumiem