├── docs ├── img │ ├── readme.md │ ├── links.jpg │ ├── mitte.png │ ├── rechts.jpg │ └── headerbild.jpeg ├── readme.md └── index.html ├── primaten.ico ├── primaten.jpg ├── primaten.res ├── primatenscreen.png ├── README.md ├── primaten.lpr ├── primaten.lpi ├── umain.pas ├── primaten.lps ├── umain.lfm ├── unitprimaten.pas ├── primaten.py └── primatenOpt.py /docs/img/readme.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/readme.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /primaten.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkoopongithub/primaten/main/primaten.ico -------------------------------------------------------------------------------- /primaten.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkoopongithub/primaten/main/primaten.jpg -------------------------------------------------------------------------------- /primaten.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkoopongithub/primaten/main/primaten.res -------------------------------------------------------------------------------- /docs/img/links.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkoopongithub/primaten/main/docs/img/links.jpg -------------------------------------------------------------------------------- /docs/img/mitte.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkoopongithub/primaten/main/docs/img/mitte.png -------------------------------------------------------------------------------- /docs/img/rechts.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkoopongithub/primaten/main/docs/img/rechts.jpg -------------------------------------------------------------------------------- /primatenscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkoopongithub/primaten/main/primatenscreen.png -------------------------------------------------------------------------------- /docs/img/headerbild.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkoopongithub/primaten/main/docs/img/headerbild.jpeg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # primaten 2 | In sociobiology, genes keep culture on a leash. When simulating primate cultures, genes and memes keep each other on a leash. Based on a random distribution of cultures, cultures develop side by side and patterns stabilize. 3 | 4 | ![screenshot](./primatenscreen.png) 5 | 6 | ![screenshot](./primaten.jpg) 7 | -------------------------------------------------------------------------------- /primaten.lpr: -------------------------------------------------------------------------------- 1 | program primaten; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | uses 6 | {$IFDEF UNIX}{$IFDEF UseCThreads} 7 | cthreads, 8 | {$ENDIF}{$ENDIF} 9 | Interfaces, // this includes the LCL widgetset 10 | Forms, UMain, unitprimaten 11 | { you can add units after this }; 12 | 13 | {$R *.res} 14 | 15 | begin 16 | RequireDerivedFormResource:=True; 17 | Application.Initialize; 18 | Application.CreateForm(TfrmMain, frmMain); 19 | Application.Run; 20 | end. 21 | 22 | -------------------------------------------------------------------------------- /primaten.lpi: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <ResourceType Value="res"/> 11 | <UseXPManifest Value="True"/> 12 | <Icon Value="0"/> 13 | </General> 14 | <i18n> 15 | <EnableI18N LFM="False"/> 16 | </i18n> 17 | <VersionInfo> 18 | <StringTable ProductVersion=""/> 19 | </VersionInfo> 20 | <BuildModes Count="1"> 21 | <Item1 Name="Default" Default="True"/> 22 | </BuildModes> 23 | <PublishOptions> 24 | <Version Value="2"/> 25 | </PublishOptions> 26 | <RunParams> 27 | <local> 28 | <FormatVersion Value="1"/> 29 | </local> 30 | </RunParams> 31 | <RequiredPackages Count="1"> 32 | <Item1> 33 | <PackageName Value="LCL"/> 34 | </Item1> 35 | </RequiredPackages> 36 | <Units Count="3"> 37 | <Unit0> 38 | <Filename Value="primaten.lpr"/> 39 | <IsPartOfProject Value="True"/> 40 | </Unit0> 41 | <Unit1> 42 | <Filename Value="umain.pas"/> 43 | <IsPartOfProject Value="True"/> 44 | <ComponentName Value="frmMain"/> 45 | <HasResources Value="True"/> 46 | <ResourceBaseClass Value="Form"/> 47 | <UnitName Value="UMain"/> 48 | </Unit1> 49 | <Unit2> 50 | <Filename Value="unitprimaten.pas"/> 51 | <IsPartOfProject Value="True"/> 52 | <UnitName Value="Unitprimaten"/> 53 | </Unit2> 54 | </Units> 55 | </ProjectOptions> 56 | <CompilerOptions> 57 | <Version Value="11"/> 58 | <PathDelim Value="\"/> 59 | <Target> 60 | <Filename Value="primaten"/> 61 | </Target> 62 | <SearchPaths> 63 | <IncludeFiles Value="$(ProjOutDir)"/> 64 | <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> 65 | </SearchPaths> 66 | <Linking> 67 | <Options> 68 | <Win32> 69 | <GraphicApplication Value="True"/> 70 | </Win32> 71 | </Options> 72 | </Linking> 73 | </CompilerOptions> 74 | <Debugging> 75 | <Exceptions Count="3"> 76 | <Item1> 77 | <Name Value="EAbort"/> 78 | </Item1> 79 | <Item2> 80 | <Name Value="ECodetoolError"/> 81 | </Item2> 82 | <Item3> 83 | <Name Value="EFOpenError"/> 84 | </Item3> 85 | </Exceptions> 86 | </Debugging> 87 | </CONFIG> 88 | -------------------------------------------------------------------------------- /umain.pas: -------------------------------------------------------------------------------- 1 | unit UMain; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | interface 6 | 7 | uses 8 | Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls, 9 | Grids, StdCtrls, Unitprimaten; 10 | 11 | type 12 | (* Datentyp fuer die virtuelle Welt*) 13 | 14 | 15 | { TfrmMain } 16 | 17 | TfrmMain = class(TForm) 18 | btnZufall: TButton; 19 | btnNext: TButton; 20 | btnStart: TButton; 21 | btnStopp: TButton; 22 | Shape1: TShape; 23 | Shape10: TShape; 24 | Shape11: TShape; 25 | Shape12: TShape; 26 | Shape13: TShape; 27 | Shape14: TShape; 28 | Shape15: TShape; 29 | Shape2: TShape; 30 | Shape3: TShape; 31 | Shape4: TShape; 32 | Shape5: TShape; 33 | Shape6: TShape; 34 | Shape7: TShape; 35 | Shape8: TShape; 36 | Shape9: TShape; 37 | Spielfeld: TDrawGrid; 38 | pnlBottom: TPanel; 39 | Kulturmonitor: TStringGrid; 40 | StaticText1: TStaticText; 41 | StaticText10: TStaticText; 42 | StaticText11: TStaticText; 43 | StaticText12: TStaticText; 44 | StaticText13: TStaticText; 45 | StaticText14: TStaticText; 46 | StaticText15: TStaticText; 47 | StaticText16: TStaticText; 48 | StaticText2: TStaticText; 49 | StaticText3: TStaticText; 50 | StaticText4: TStaticText; 51 | StaticText5: TStaticText; 52 | StaticText6: TStaticText; 53 | StaticText7: TStaticText; 54 | StaticText8: TStaticText; 55 | StaticText9: TStaticText; 56 | tmrAnimation: TTimer; 57 | procedure Auswerten(Sender: TObject); 58 | procedure btnStoppClick(Sender: TObject); 59 | procedure btnStartClick(Sender: TObject); 60 | procedure btnZufallClick(Sender: TObject); 61 | procedure FormClose(Sender: TObject; var CloseAction: TCloseAction); 62 | procedure FormCreate(Sender: TObject); 63 | procedure KulturmonitorDrawCell(Sender: TObject; aCol, aRow: Integer; 64 | aRect: TRect; aState: TGridDrawState); 65 | procedure SpielfeldDrawCell(Sender: TObject; aCol, aRow: Integer; 66 | aRect: TRect; aState: TGridDrawState); 67 | procedure StaticText3Click(Sender: TObject); 68 | 69 | private 70 | { private declarations } 71 | 72 | (* virtuelle Spielwelt *) 73 | 74 | 75 | public 76 | { public declarations } 77 | end; 78 | PROCEDURE spiel(VAR von,nach :raum); 79 | var 80 | frmMain: TfrmMain; 81 | 82 | implementation 83 | 84 | {$R *.lfm} 85 | 86 | { TfrmMain } 87 | 88 | procedure TfrmMain.FormCreate(Sender: TObject); 89 | begin 90 | randomize; 91 | aufbau; 92 | FillByte (a,SizeOf(a),0); 93 | FillByte (b,SizeOf(b),0); 94 | zufall(a); 95 | Spielfeld.Repaint; 96 | end; 97 | 98 | procedure TfrmMain.KulturmonitorDrawCell(Sender: TObject; aCol, aRow: Integer; 99 | aRect: TRect; aState: TGridDrawState); 100 | var i:integer; 101 | begin 102 | i:= a[aCol+1, aRow+1].kultur; 103 | 104 | case i of 105 | 0: Kulturmonitor.Canvas.Brush.Color := clWhite; 106 | 1: Kulturmonitor.Canvas.Brush.Color := clMaroon; 107 | 2: Kulturmonitor.Canvas.Brush.Color := clGreen; 108 | 3: Kulturmonitor.Canvas.Brush.Color := clOlive; 109 | 4: Kulturmonitor.Canvas.Brush.Color := clNavy; 110 | 5: Kulturmonitor.Canvas.Brush.Color := clPurple; 111 | 6: Kulturmonitor.Canvas.Brush.Color := clTeal; 112 | 7: Kulturmonitor.Canvas.Brush.Color := clRed; 113 | 8: Kulturmonitor.Canvas.Brush.Color := clLime; 114 | 9: Kulturmonitor.Canvas.Brush.Color := clYellow; 115 | 116 | end; 117 | 118 | 119 | Kulturmonitor.Canvas.FillRect(aRect); 120 | end; 121 | 122 | 123 | 124 | procedure TfrmMain.SpielfeldDrawCell(Sender: TObject; aCol, aRow: Integer; 125 | aRect: TRect; aState: TGridDrawState); 126 | begin 127 | if a[aCol+1, aRow+1].status = 1 then 128 | begin 129 | if a[aCol+1, aRow+1].geschlecht = 2 then 130 | Spielfeld.Canvas.Brush.Color := clFuchsia 131 | else 132 | if a[aCol+1, aRow+1].geschlecht= 1 then 133 | Spielfeld.Canvas.Brush.Color := clLime 134 | 135 | end 136 | else 137 | begin 138 | if a[aCol+1, aRow+1].status = 2 then 139 | begin 140 | if a[aCol+1, aRow+1].geschlecht = 2 then 141 | Spielfeld.Canvas.Brush.Color := clRed 142 | else 143 | if a[aCol+1, aRow+1].geschlecht = 1 then 144 | Spielfeld.Canvas.Brush.Color := clGreen 145 | 146 | end 147 | else 148 | Spielfeld.Canvas.Brush.Color := clWhite 149 | end; 150 | 151 | Spielfeld.Canvas.FillRect(aRect); 152 | 153 | end; 154 | 155 | procedure TfrmMain.StaticText3Click(Sender: TObject); 156 | begin 157 | 158 | end; 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | procedure TfrmMain.Auswerten(Sender: TObject); 167 | 168 | begin 169 | 170 | spiel(a,b); 171 | Spielfeld.Refresh; 172 | Kulturmonitor.Refresh; 173 | a:= b; 174 | end; 175 | 176 | procedure TfrmMain.btnStoppClick(Sender: TObject); 177 | begin 178 | tmrAnimation.Enabled := false; 179 | a:=b; 180 | Spielfeld.Refresh; 181 | Kulturmonitor.Refresh; 182 | end; 183 | 184 | procedure TfrmMain.btnStartClick(Sender: TObject); 185 | begin 186 | tmrAnimation.Enabled := true; 187 | end; 188 | 189 | procedure TfrmMain.btnZufallClick(Sender: TObject); 190 | begin 191 | zufall(a); 192 | Spielfeld.Repaint; 193 | Kulturmonitor.Repaint; 194 | end; 195 | 196 | procedure TfrmMain.FormClose(Sender: TObject; var CloseAction: TCloseAction); 197 | begin 198 | tmrAnimation.Enabled := false; 199 | x := xa; 200 | abbaux(x); 201 | y := ya; 202 | abbauy(y); 203 | end; 204 | PROCEDURE spiel(VAR von,nach :raum); 205 | BEGIN 206 | y :=ya; 207 | x :=xa; 208 | REPEAT 209 | REPEAT 210 | nach(.x^.i,y^.i.):=neu(von,x,y); 211 | frmMain.Kulturmonitor.Cells[x^.i-1,y^.i-1]:=IntToSTR(nach(.x^.i,y^.i.).kultur); 212 | x := x^.n 213 | UNTIL x =xa; 214 | y := y^.n 215 | UNTIL y =ya; 216 | END; 217 | end. 218 | 219 | -------------------------------------------------------------------------------- /primaten.lps: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <CONFIG> 3 | <ProjectSession> 4 | <PathDelim Value="\"/> 5 | <Version Value="10"/> 6 | <BuildModes Active="Default"/> 7 | <Units Count="4"> 8 | <Unit0> 9 | <Filename Value="primaten.lpr"/> 10 | <IsPartOfProject Value="True"/> 11 | <EditorIndex Value="3"/> 12 | <CursorPos X="25" Y="10"/> 13 | <UsageCount Value="52"/> 14 | <Loaded Value="True"/> 15 | </Unit0> 16 | <Unit1> 17 | <Filename Value="umain.pas"/> 18 | <IsPartOfProject Value="True"/> 19 | <ComponentName Value="frmMain"/> 20 | <HasResources Value="True"/> 21 | <ResourceBaseClass Value="Form"/> 22 | <UnitName Value="UMain"/> 23 | <TopLine Value="17"/> 24 | <CursorPos X="27" Y="196"/> 25 | <ComponentState Value="1"/> 26 | <UsageCount Value="52"/> 27 | <Loaded Value="True"/> 28 | <LoadedDesigner Value="True"/> 29 | </Unit1> 30 | <Unit2> 31 | <Filename Value="unitprimaten.pas"/> 32 | <IsPartOfProject Value="True"/> 33 | <UnitName Value="Unitprimaten"/> 34 | <IsVisibleTab Value="True"/> 35 | <EditorIndex Value="2"/> 36 | <TopLine Value="297"/> 37 | <CursorPos Y="326"/> 38 | <UsageCount Value="52"/> 39 | <Loaded Value="True"/> 40 | </Unit2> 41 | <Unit3> 42 | <Filename Value="C:\lazarus\lcl\include\control.inc"/> 43 | <EditorIndex Value="1"/> 44 | <TopLine Value="3646"/> 45 | <CursorPos Y="3668"/> 46 | <UsageCount Value="25"/> 47 | <Loaded Value="True"/> 48 | </Unit3> 49 | </Units> 50 | <JumpHistory Count="30" HistoryIndex="29"> 51 | <Position1> 52 | <Filename Value="umain.pas"/> 53 | <Caret Line="97" Column="3" TopLine="93"/> 54 | </Position1> 55 | <Position2> 56 | <Filename Value="umain.pas"/> 57 | <Caret Line="98" Column="3" TopLine="94"/> 58 | </Position2> 59 | <Position3> 60 | <Filename Value="umain.pas"/> 61 | <Caret Line="99" Column="3" TopLine="95"/> 62 | </Position3> 63 | <Position4> 64 | <Filename Value="umain.pas"/> 65 | <Caret Line="100" Column="3" TopLine="96"/> 66 | </Position4> 67 | <Position5> 68 | <Filename Value="umain.pas"/> 69 | <Caret Line="101" Column="3" TopLine="97"/> 70 | </Position5> 71 | <Position6> 72 | <Filename Value="umain.pas"/> 73 | <Caret Line="102" Column="3" TopLine="98"/> 74 | </Position6> 75 | <Position7> 76 | <Filename Value="umain.pas"/> 77 | <Caret Line="101" Column="3" TopLine="97"/> 78 | </Position7> 79 | <Position8> 80 | <Filename Value="umain.pas"/> 81 | <Caret Line="100" Column="3"/> 82 | </Position8> 83 | <Position9> 84 | <Filename Value="umain.pas"/> 85 | <Caret Line="101" Column="3" TopLine="57"/> 86 | </Position9> 87 | <Position10> 88 | <Filename Value="umain.pas"/> 89 | <Caret Line="103" Column="3" TopLine="103"/> 90 | </Position10> 91 | <Position11> 92 | <Filename Value="umain.pas"/> 93 | <Caret Line="158" Column="3" TopLine="156"/> 94 | </Position11> 95 | <Position12> 96 | <Filename Value="unitprimaten.pas"/> 97 | <Caret Line="217" Column="16" TopLine="195"/> 98 | </Position12> 99 | <Position13> 100 | <Filename Value="unitprimaten.pas"/> 101 | <Caret Line="248" Column="13" TopLine="231"/> 102 | </Position13> 103 | <Position14> 104 | <Filename Value="unitprimaten.pas"/> 105 | <Caret Line="258" TopLine="237"/> 106 | </Position14> 107 | <Position15> 108 | <Filename Value="unitprimaten.pas"/> 109 | <Caret Line="311" Column="55" TopLine="271"/> 110 | </Position15> 111 | <Position16> 112 | <Filename Value="umain.pas"/> 113 | <Caret Line="159" Column="16" TopLine="143"/> 114 | </Position16> 115 | <Position17> 116 | <Filename Value="umain.pas"/> 117 | <Caret Line="131" Column="30" TopLine="120"/> 118 | </Position17> 119 | <Position18> 120 | <Filename Value="umain.pas"/> 121 | <Caret Line="166" TopLine="156"/> 122 | </Position18> 123 | <Position19> 124 | <Filename Value="umain.pas"/> 125 | <Caret Line="57" TopLine="36"/> 126 | </Position19> 127 | <Position20> 128 | <Filename Value="unitprimaten.pas"/> 129 | <Caret Line="291" Column="34" TopLine="173"/> 130 | </Position20> 131 | <Position21> 132 | <Filename Value="unitprimaten.pas"/> 133 | <Caret Line="288" Column="50" TopLine="272"/> 134 | </Position21> 135 | <Position22> 136 | <Filename Value="unitprimaten.pas"/> 137 | <Caret Line="229" Column="34" TopLine="205"/> 138 | </Position22> 139 | <Position23> 140 | <Filename Value="umain.pas"/> 141 | <Caret Line="172" Column="27" TopLine="87"/> 142 | </Position23> 143 | <Position24> 144 | <Filename Value="unitprimaten.pas"/> 145 | <Caret Line="229" Column="53" TopLine="194"/> 146 | </Position24> 147 | <Position25> 148 | <Filename Value="unitprimaten.pas"/> 149 | <Caret Line="94" Column="11" TopLine="94"/> 150 | </Position25> 151 | <Position26> 152 | <Filename Value="unitprimaten.pas"/> 153 | <Caret Line="229" Column="45" TopLine="196"/> 154 | </Position26> 155 | <Position27> 156 | <Filename Value="unitprimaten.pas"/> 157 | <Caret Line="163" Column="394" TopLine="146"/> 158 | </Position27> 159 | <Position28> 160 | <Filename Value="unitprimaten.pas"/> 161 | <Caret Line="231" Column="45" TopLine="198"/> 162 | </Position28> 163 | <Position29> 164 | <Filename Value="unitprimaten.pas"/> 165 | <Caret Line="154" Column="3" TopLine="141"/> 166 | </Position29> 167 | <Position30> 168 | <Filename Value="unitprimaten.pas"/> 169 | <Caret Line="232" Column="53" TopLine="205"/> 170 | </Position30> 171 | </JumpHistory> 172 | </ProjectSession> 173 | </CONFIG> 174 | -------------------------------------------------------------------------------- /umain.lfm: -------------------------------------------------------------------------------- 1 | object frmMain: TfrmMain 2 | Left = 398 3 | Height = 555 4 | Top = 164 5 | Width = 1059 6 | BorderStyle = bsDialog 7 | Caption = 'Primaten' 8 | ClientHeight = 555 9 | ClientWidth = 1059 10 | OnClose = FormClose 11 | OnCreate = FormCreate 12 | ParentFont = True 13 | LCLVersion = '1.6.4.0' 14 | object pnlBottom: TPanel 15 | Left = 0 16 | Height = 45 17 | Top = 510 18 | Width = 1059 19 | Align = alBottom 20 | ClientHeight = 45 21 | ClientWidth = 1059 22 | TabOrder = 0 23 | object btnNext: TButton 24 | Left = 8 25 | Height = 33 26 | Top = 6 27 | Width = 89 28 | Caption = '&Nächster Zeittakt' 29 | OnClick = Auswerten 30 | TabOrder = 0 31 | end 32 | object btnStart: TButton 33 | Left = 110 34 | Height = 33 35 | Top = 6 36 | Width = 89 37 | Caption = '&Start' 38 | OnClick = btnStartClick 39 | TabOrder = 1 40 | end 41 | object btnStopp: TButton 42 | Left = 206 43 | Height = 33 44 | Top = 5 45 | Width = 89 46 | Caption = 'Sto&pp' 47 | OnClick = btnStoppClick 48 | TabOrder = 2 49 | end 50 | object btnZufall: TButton 51 | Left = 304 52 | Height = 33 53 | Top = 6 54 | Width = 89 55 | Caption = '&Zufall' 56 | OnClick = btnZufallClick 57 | TabOrder = 3 58 | end 59 | end 60 | object Spielfeld: TDrawGrid 61 | Left = 0 62 | Height = 248 63 | Top = 0 64 | Width = 808 65 | ColCount = 80 66 | DefaultColWidth = 10 67 | DefaultDrawing = False 68 | DefaultRowHeight = 10 69 | ExtendedSelect = False 70 | FixedCols = 0 71 | FixedRows = 0 72 | Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine] 73 | RowCount = 24 74 | TabOrder = 1 75 | OnDrawCell = SpielfeldDrawCell 76 | end 77 | object Kulturmonitor: TStringGrid 78 | Left = 0 79 | Height = 248 80 | Top = 256 81 | Width = 808 82 | ColCount = 80 83 | DefaultColWidth = 10 84 | DefaultRowHeight = 10 85 | FixedCols = 0 86 | FixedRows = 0 87 | RowCount = 24 88 | TabOrder = 2 89 | OnDrawCell = KulturmonitorDrawCell 90 | end 91 | object Shape1: TShape 92 | Left = 884 93 | Height = 65 94 | Top = 8 95 | Width = 65 96 | Brush.Color = clLime 97 | end 98 | object Shape2: TShape 99 | Left = 816 100 | Height = 65 101 | Top = 77 102 | Width = 65 103 | Brush.Color = clRed 104 | end 105 | object Shape3: TShape 106 | Left = 884 107 | Height = 65 108 | Top = 77 109 | Width = 65 110 | Brush.Color = clFuchsia 111 | end 112 | object Shape4: TShape 113 | Left = 816 114 | Height = 65 115 | Top = 8 116 | Width = 65 117 | Brush.Color = clGreen 118 | end 119 | object StaticText1: TStaticText 120 | Left = 958 121 | Height = 17 122 | Top = 36 123 | Width = 65 124 | Caption = 'weiblich' 125 | TabOrder = 3 126 | end 127 | object StaticText2: TStaticText 128 | Left = 958 129 | Height = 17 130 | Top = 88 131 | Width = 65 132 | Caption = 'männlich' 133 | TabOrder = 4 134 | end 135 | object StaticText3: TStaticText 136 | Left = 816 137 | Height = 17 138 | Top = 144 139 | Width = 137 140 | Caption = 'Silberrücken' 141 | OnClick = StaticText3Click 142 | TabOrder = 5 143 | end 144 | object StaticText4: TStaticText 145 | Left = 888 146 | Height = 17 147 | Top = 144 148 | Width = 65 149 | Caption = 'Jungtier' 150 | TabOrder = 6 151 | end 152 | object StaticText5: TStaticText 153 | Left = 818 154 | Height = 17 155 | Top = 269 156 | Width = 176 157 | Caption = 'Kulturmonitor / Primatenkulturen' 158 | TabOrder = 7 159 | end 160 | object Shape6: TShape 161 | Left = 818 162 | Height = 13 163 | Top = 296 164 | Width = 15 165 | end 166 | object Shape7: TShape 167 | Left = 818 168 | Height = 13 169 | Top = 312 170 | Width = 15 171 | Brush.Color = clMaroon 172 | end 173 | object Shape8: TShape 174 | Left = 818 175 | Height = 13 176 | Top = 328 177 | Width = 15 178 | Brush.Color = clGreen 179 | end 180 | object Shape9: TShape 181 | Left = 818 182 | Height = 13 183 | Top = 344 184 | Width = 15 185 | Brush.Color = clOlive 186 | end 187 | object Shape10: TShape 188 | Left = 818 189 | Height = 13 190 | Top = 360 191 | Width = 15 192 | Brush.Color = clNavy 193 | end 194 | object Shape11: TShape 195 | Left = 818 196 | Height = 13 197 | Top = 376 198 | Width = 15 199 | Brush.Color = clPurple 200 | end 201 | object Shape12: TShape 202 | Left = 818 203 | Height = 13 204 | Top = 392 205 | Width = 15 206 | Brush.Color = clTeal 207 | end 208 | object Shape13: TShape 209 | Left = 818 210 | Height = 13 211 | Top = 408 212 | Width = 15 213 | Brush.Color = clRed 214 | end 215 | object Shape14: TShape 216 | Left = 818 217 | Height = 13 218 | Top = 424 219 | Width = 15 220 | Brush.Color = clLime 221 | end 222 | object StaticText6: TStaticText 223 | Left = 840 224 | Height = 17 225 | Top = 296 226 | Width = 86 227 | Caption = '0 leeres Feld' 228 | TabOrder = 8 229 | end 230 | object Shape5: TShape 231 | Left = 816 232 | Height = 65 233 | Top = 176 234 | Width = 65 235 | end 236 | object StaticText7: TStaticText 237 | Left = 884 238 | Height = 17 239 | Top = 192 240 | Width = 134 241 | Caption = 'leeres Feld / kein Primat' 242 | TabOrder = 9 243 | end 244 | object StaticText8: TStaticText 245 | Left = 840 246 | Height = 17 247 | Top = 312 248 | Width = 86 249 | Caption = 'Kultur 1' 250 | TabOrder = 10 251 | end 252 | object StaticText9: TStaticText 253 | Left = 840 254 | Height = 17 255 | Top = 328 256 | Width = 86 257 | Caption = 'Kultur 2' 258 | TabOrder = 11 259 | end 260 | object StaticText10: TStaticText 261 | Left = 840 262 | Height = 17 263 | Top = 344 264 | Width = 86 265 | Caption = 'Kultur 3' 266 | TabOrder = 12 267 | end 268 | object StaticText11: TStaticText 269 | Left = 840 270 | Height = 17 271 | Top = 360 272 | Width = 86 273 | Caption = 'Kultur 4' 274 | TabOrder = 13 275 | end 276 | object StaticText12: TStaticText 277 | Left = 840 278 | Height = 17 279 | Top = 376 280 | Width = 86 281 | Caption = 'Kultur 5' 282 | TabOrder = 14 283 | end 284 | object StaticText13: TStaticText 285 | Left = 840 286 | Height = 17 287 | Top = 392 288 | Width = 86 289 | Caption = 'Kultur 6' 290 | TabOrder = 15 291 | end 292 | object StaticText14: TStaticText 293 | Left = 840 294 | Height = 17 295 | Top = 408 296 | Width = 86 297 | Caption = 'Kultur 7' 298 | TabOrder = 16 299 | end 300 | object StaticText15: TStaticText 301 | Left = 840 302 | Height = 17 303 | Top = 424 304 | Width = 86 305 | Caption = 'Kultur 8' 306 | TabOrder = 17 307 | end 308 | object Shape15: TShape 309 | Left = 818 310 | Height = 13 311 | Top = 440 312 | Width = 15 313 | Brush.Color = clYellow 314 | end 315 | object StaticText16: TStaticText 316 | Left = 840 317 | Height = 17 318 | Top = 440 319 | Width = 86 320 | Caption = 'Kultur 9' 321 | TabOrder = 18 322 | end 323 | object tmrAnimation: TTimer 324 | Interval = 40 325 | OnTimer = Auswerten 326 | left = 8 327 | top = 8 328 | end 329 | end 330 | -------------------------------------------------------------------------------- /unitprimaten.pas: -------------------------------------------------------------------------------- 1 | unit Unitprimaten; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | interface 6 | 7 | uses 8 | Classes, SysUtils; 9 | 10 | (******************************************************************) 11 | (* Paul Koop M.A. Primaten *) 12 | (* Die Simulation wurde ursprunglich entwickelt, *) 13 | (* um die Verwendbarkeit von Zellularautomaten *) 14 | (* fuer die Algorithmisch Rekursive Sequanzanalyse *) 15 | (* zu ueberpruefen *) 16 | (* Modellcharakter hat allein der Quelltext. Eine Compilierung *) 17 | (* dient nur als Falsifikationsversuch *) 18 | (******************************************************************) 19 | 20 | (*------------------------------------ Datenstruktur -----------------*) 21 | 22 | TYPE 23 | primat = RECORD 24 | status, (* 0,1,2 leer, jungtier, erfahrenes tier *) 25 | alter, (* 0...9 *) 26 | geschlecht, (* 0 1 w 2 m *) 27 | kultur, (* 0..9 *) 28 | macht :0..9;(* 0..9 *) 29 | END; 30 | 31 | raum = array[1..80,1..24] of primat; 32 | zahl = ^inhalt; 33 | inhalt = RECORD 34 | i:integer; 35 | v:zahl; 36 | n:zahl; 37 | END; 38 | VAR 39 | a,b:raum; 40 | n,x,y,xa,ya:zahl; 41 | 42 | 43 | PROCEDURE aufbau; 44 | PROCEDURE abbaux(x:zahl); 45 | PROCEDURE abbauy(y:zahl); 46 | FUNCTION neu (VAR r:raum; VAR x,y:zahl):primat; 47 | PROCEDURE zufall(VAR von:raum); 48 | 49 | 50 | implementation 51 | 52 | 53 | USES dos,crt; 54 | 55 | 56 | 57 | (*---------------------------------------- Prozeduren ---------------*) 58 | PROCEDURE aufbau; 59 | VAR z:integer; 60 | BEGIN 61 | z := 1; 62 | new(n); 63 | xa := n; 64 | x := n; 65 | x^.i := z; 66 | REPEAT 67 | z := z +1; 68 | new(n); 69 | x^.n := n; 70 | n^.v := x; 71 | x := n; 72 | x^.i := z; 73 | UNTIL z = 80; 74 | x^.n := xa; 75 | xa^.v := x; 76 | 77 | z := 1; 78 | new(n); 79 | ya := n; 80 | y := n; 81 | y^.i := z; 82 | REPEAT 83 | z := z +1; 84 | new(n); 85 | y^.n := n; 86 | n^.v := y; 87 | y := n; 88 | y^.i := z; 89 | UNTIL z = 24; 90 | y^.n := ya; 91 | ya^.v := y; 92 | END; 93 | 94 | PROCEDURE abbaux(x:zahl); 95 | BEGIN 96 | IF x^.n <> xa THEN abbaux(x^.n); 97 | dispose(x); 98 | END; 99 | 100 | PROCEDURE abbauy(y:zahl); 101 | BEGIN 102 | IF y^.n <> ya THEN abbauy(y^.n); 103 | dispose(y); 104 | END; 105 | FUNCTION neu (VAR r:raum; VAR x,y:zahl):primat; 106 | VAR z:integer; 107 | FUNCTION umgebungleer (VAR r:raum; VAR x,y:zahl):boolean; 108 | VAR z:integer; 109 | BEGIN 110 | z := 0; 111 | IF r(.x^.v^.i,y^.v^.i.).kultur <>r(.x^.i,y^.i.).kultur THEN z := z +1; 112 | IF r(.x^.i ,y^.v^.i.).kultur <>r(.x^.i,y^.i.).kultur THEN z := z +1; 113 | IF r(.x^.n^.i,y^.v^.i.).kultur <>r(.x^.i,y^.i.).kultur THEN z := z +1; 114 | IF r(.x^.v^.i,y^.i .).kultur <>r(.x^.i,y^.i.).kultur THEN z := z +1; 115 | IF r(.x^.n^.i,y^.i .).kultur <>r(.x^.i,y^.i.).kultur THEN z := z +1; 116 | IF r(.x^.v^.i,y^.n^.i.).kultur <>r(.x^.i,y^.i.).kultur THEN z := z +1; 117 | IF r(.x^.i ,y^.n^.i.).kultur <>r(.x^.i,y^.i.).kultur THEN z := z +1; 118 | IF r(.x^.n^.i,y^.n^.i.).kultur <>r(.x^.i,y^.i.).kultur THEN z := z +1; 119 | IF z=8 120 | THEN umgebungleer:= true 121 | ELSE umgebungleer:= false; 122 | END; 123 | 124 | FUNCTION umgebungmaennlichererwachsener (VAR r:raum; VAR x,y:zahl):boolean; 125 | VAR z:integer; 126 | BEGIN 127 | z := 0; 128 | IF (r(.x^.v^.i,y^.v^.i.).status =2) and (r(.x^.v^.i,y^.v^.i.).geschlecht =2) and (r(.x^.v^.i,y^.v^.i.).kultur<> r(.x^.i,y^.i.).kultur) THEN z := z +1; 129 | IF (r(.x^.i ,y^.v^.i.).status =2) and (r(.x^.i ,y^.v^.i.).geschlecht =2) and (r(.x^.i ,y^.v^.i.).kultur<> r(.x^.i,y^.i.).kultur) THEN z := z +1; 130 | IF (r(.x^.n^.i,y^.v^.i.).status =2) and (r(.x^.n^.i,y^.v^.i.).geschlecht =2) and (r(.x^.n^.i,y^.v^.i.).kultur<> r(.x^.i,y^.i.).kultur) THEN z := z +1; 131 | IF (r(.x^.v^.i,y^.i .).status =2) and (r(.x^.v^.i,y^.i .).geschlecht =2) and (r(.x^.v^.i,y^.i .).kultur<> r(.x^.i,y^.i.).kultur) THEN z := z +1; 132 | IF (r(.x^.n^.i,y^.i .).status =2) and (r(.x^.n^.i,y^.i .).geschlecht =2) and (r(.x^.n^.i,y^.i .).kultur<> r(.x^.i,y^.i.).kultur) THEN z := z +1; 133 | IF (r(.x^.v^.i,y^.n^.i.).status =2) and (r(.x^.v^.i,y^.n^.i.).geschlecht =2) and (r(.x^.v^.i,y^.n^.i.).kultur<> r(.x^.i,y^.i.).kultur) THEN z := z +1; 134 | IF (r(.x^.i ,y^.n^.i.).status =2) and (r(.x^.i ,y^.n^.i.).geschlecht =2) and (r(.x^.i ,y^.n^.i.).kultur<> r(.x^.i,y^.i.).kultur) THEN z := z +1; 135 | IF (r(.x^.n^.i,y^.n^.i.).status =2) and (r(.x^.i ,y^.n^.i.).geschlecht =2) and (r(.x^.i ,y^.n^.i.).kultur<> r(.x^.i,y^.i.).kultur) THEN z := z +1; 136 | IF z=0 137 | THEN Umgebungmaennlichererwachsener:= false 138 | ELSE Umgebungmaennlichererwachsener:= true; 139 | END; 140 | 141 | PROCEDURE kind (VAR r:raum; VAR x,y:zahl); 142 | TYPE 143 | elter= RECORD 144 | m,w: boolean; 145 | macht:0..9; 146 | END; 147 | 148 | VAR 149 | i,ym,k:integer; 150 | eltern: array(.1..9.) of elter; 151 | 152 | 153 | BEGIN 154 | 155 | FOR i:=1 TO 9 DO BEGIN eltern[i].w:=false; eltern[i].m:=false;eltern[i].macht:=0; END; 156 | 157 | IF (r(.x^.v^.i,y^.v^.i.).status =2) THEN BEGIN IF r(.x^.v^.i,y^.v^.i.).geschlecht= 1 THEN eltern[r(.x^.v^.i,y^.v^.i.).kultur].w:=true ELSE IF r(.x^.v^.i,y^.v^.i.).geschlecht= 2 THEN BEGIN eltern[r(.x^.v^.i,y^.v^.i.).kultur].m:=true; IF r(.x^.v^.i,y^.v^.i.).macht> eltern[r(.x^.v^.i,y^.v^.i.).kultur].macht THEN eltern[r(.x^.v^.i,y^.v^.i.).kultur].macht:=r(.x^.v^.i,y^.v^.i.).macht END; END; 158 | IF (r(.x^.i ,y^.v^.i.).status =2) THEN BEGIN IF r(.x^.i ,y^.v^.i.).geschlecht= 1 THEN eltern[r(.x^.i ,y^.v^.i.).kultur].w:=true ELSE IF r(.x^.i ,y^.v^.i.).geschlecht= 2 THEN BEGIN eltern[r(.x^.i ,y^.v^.i.).kultur].m:=true; IF r(.x^.i ,y^.v^.i.).macht> eltern[r(.x^.i ,y^.v^.i.).kultur].macht THEN eltern[r(.x^.i ,y^.v^.i.).kultur].macht:=r(.x^.i ,y^.v^.i.).macht END; END; 159 | IF (r(.x^.n^.i,y^.v^.i.).status =2) THEN BEGIN IF r(.x^.n^.i,y^.v^.i.).geschlecht= 1 THEN eltern[r(.x^.n^.i,y^.v^.i.).kultur].w:=true ELSE IF r(.x^.n^.i,y^.v^.i.).geschlecht= 2 THEN BEGIN eltern[r(.x^.n^.i,y^.v^.i.).kultur].m:=true; IF r(.x^.n^.i,y^.v^.i.).macht> eltern[r(.x^.n^.i,y^.v^.i.).kultur].macht THEN eltern[r(.x^.n^.i,y^.v^.i.).kultur].macht:=r(.x^.n^.i,y^.v^.i.).macht END; END; 160 | IF (r(.x^.v^.i,y^.i .).status =2) THEN BEGIN IF r(.x^.v^.i,y^.i .).geschlecht= 1 THEN eltern[r(.x^.v^.i,y^.i .).kultur].w:=true ELSE IF r(.x^.v^.i,y^.i .).geschlecht= 2 THEN BEGIN eltern[r(.x^.v^.i,y^.i .).kultur].m:=true; IF r(.x^.v^.i,y^.i .).macht> eltern[r(.x^.v^.i,y^.i .).kultur].macht THEN eltern[r(.x^.v^.i,y^.i .).kultur].macht:=r(.x^.v^.i,y^.i .).macht END; END; 161 | IF (r(.x^.n^.i,y^.i .).status =2) THEN BEGIN IF r(.x^.n^.i,y^.i .).geschlecht= 1 THEN eltern[r(.x^.n^.i,y^.i .).kultur].w:=true ELSE IF r(.x^.n^.i,y^.i .).geschlecht= 2 THEN BEGIN eltern[r(.x^.n^.i,y^.i .).kultur].m:=true; IF r(.x^.n^.i,y^.i .).macht> eltern[r(.x^.n^.i,y^.i .).kultur].macht THEN eltern[r(.x^.n^.i,y^.i .).kultur].macht:=r(.x^.n^.i,y^.i .).macht END; END; 162 | IF (r(.x^.v^.i,y^.n^.i.).status =2) THEN BEGIN IF r(.x^.v^.i,y^.n^.i.).geschlecht= 1 THEN eltern[r(.x^.v^.i,y^.n^.i.).kultur].w:=true ELSE IF r(.x^.v^.i,y^.n^.i.).geschlecht= 2 THEN BEGIN eltern[r(.x^.v^.i,y^.n^.i.).kultur].m:=true; IF r(.x^.v^.i,y^.n^.i.).macht> eltern[r(.x^.v^.i,y^.n^.i.).kultur].macht THEN eltern[r(.x^.v^.i,y^.n^.i.).kultur].macht:=r(.x^.v^.i,y^.n^.i.).macht END; END; 163 | IF (r(.x^.i ,y^.n^.i.).status =2) THEN BEGIN IF r(.x^.i ,y^.n^.i.).geschlecht= 1 THEN eltern[r(.x^.i ,y^.n^.i.).kultur].w:=true ELSE IF r(.x^.i ,y^.n^.i.).geschlecht= 2 THEN BEGIN eltern[r(.x^.i ,y^.n^.i.).kultur].m:=true; IF r(.x^.i ,y^.n^.i.).macht> eltern[r(.x^.i ,y^.n^.i.).kultur].macht THEN eltern[r(.x^.i ,y^.n^.i.).kultur].macht:=r(.x^.i ,y^.n^.i.).macht END; END; 164 | IF (r(.x^.n^.i,y^.n^.i.).status =2) THEN BEGIN IF r(.x^.n^.i,y^.n^.i.).geschlecht= 1 THEN eltern[r(.x^.n^.i,y^.n^.i.).kultur].w:=true ELSE IF r(.x^.n^.i,y^.n^.i.).geschlecht= 2 THEN BEGIN eltern[r(.x^.n^.i,y^.n^.i.).kultur].m:=true; IF r(.x^.n^.i,y^.n^.i.).macht> eltern[r(.x^.n^.i,y^.n^.i.).kultur].macht THEN eltern[r(.x^.n^.i,y^.n^.i.).kultur].macht:=r(.x^.n^.i,y^.n^.i.).macht END; END; 165 | ym:=0;k:=0; 166 | FOR i:=1 TO 9 DO 167 | BEGIN 168 | IF (eltern[i].w=true and eltern[i].m=true) 169 | THEN 170 | BEGIN 171 | IF eltern[i].macht >ym THEN BEGIN ym:=eltern[i].macht;k:=i END 172 | END 173 | END; 174 | IF ((ym>0)and(k>0)) THEN 175 | BEGIN 176 | neu.Status:= 1; 177 | neu.alter:=0; 178 | neu.macht:=ym; 179 | neu.geschlecht:=random(2)+1; 180 | neu.kultur:=k; 181 | END 182 | ELSE 183 | neu:=r(.x^.i,y^.i.); 184 | 185 | END; 186 | 187 | FUNCTION kulturstaerkstesmaennchenannehmen (VAR r:raum; VAR x,y:zahl):integer; 188 | VAR z:integer; 189 | BEGIN 190 | z := 0; 191 | IF (r(.x^.v^.i,y^.v^.i.).status =2) and (r(.x^.v^.i,y^.v^.i.).macht>z) and (r(.x^.v^.i,y^.v^.i.).geschlecht=2) THEN z := r(.x^.v^.i,y^.v^.i.).kultur; 192 | IF (r(.x^.i ,y^.v^.i.).status =2) and (r(.x^.i ,y^.v^.i.).macht>z) and (r(.x^.i ,y^.v^.i.).geschlecht=2) THEN z := r(.x^.i ,y^.v^.i.).kultur; 193 | IF (r(.x^.n^.i,y^.v^.i.).status =2) and (r(.x^.n^.i,y^.v^.i.).macht>z) and (r(.x^.n^.i,y^.v^.i.).geschlecht=2) THEN z := r(.x^.n^.i,y^.v^.i.).kultur; 194 | IF (r(.x^.v^.i,y^.i .).status =2) and (r(.x^.v^.i,y^.i .).macht>z) and (r(.x^.v^.i,y^.i .).geschlecht=2) THEN z := r(.x^.v^.i,y^.i .).kultur; 195 | IF (r(.x^.n^.i,y^.i .).status =2) and (r(.x^.n^.i,y^.i .).macht>z) and (r(.x^.n^.i,y^.i .).geschlecht=2) THEN z := r(.x^.n^.i,y^.i .).kultur; 196 | IF (r(.x^.v^.i,y^.n^.i.).status =2) and (r(.x^.v^.i,y^.n^.i.).macht>z) and (r(.x^.v^.i,y^.n^.i.).geschlecht=2) THEN z := r(.x^.v^.i,y^.n^.i.).kultur; 197 | IF (r(.x^.i ,y^.n^.i.).status =2) and (r(.x^.i ,y^.n^.i.).macht>z) and (r(.x^.i ,y^.n^.i.).geschlecht=2) THEN z := r(.x^.i ,y^.n^.i.).kultur; 198 | IF (r(.x^.n^.i,y^.n^.i.).status =2) and (r(.x^.n^.i,y^.n^.i.).macht>z) and (r(.x^.n^.i,y^.n^.i.).geschlecht=2) THEN z := r(.x^.n^.i,y^.n^.i.).kultur; 199 | IF z=0 200 | THEN kulturstaerkstesmaennchenannehmen:= 0 201 | ELSE kulturstaerkstesmaennchenannehmen:= z; 202 | END; 203 | 204 | FUNCTION staerkeresmaennchen (VAR r:raum; VAR x,y:zahl):boolean; 205 | VAR i,z:integer; 206 | BEGIN 207 | i:=0;z := r(.x^.i,y^.i.).macht; 208 | IF (r(.x^.v^.i,y^.v^.i.).status =2) and (r(.x^.v^.i,y^.v^.i.).macht>z) and (r(.x^.v^.i,y^.v^.i.).geschlecht=2) THEN i:=i+1; 209 | IF (r(.x^.i ,y^.v^.i.).status =2) and (r(.x^.i ,y^.v^.i.).macht>z) and (r(.x^.i ,y^.v^.i.).geschlecht=2) THEN i:=i+1; 210 | IF (r(.x^.n^.i,y^.v^.i.).status =2) and (r(.x^.n^.i,y^.v^.i.).macht>z) and (r(.x^.n^.i,y^.v^.i.).geschlecht=2) THEN i:=i+1; 211 | IF (r(.x^.v^.i,y^.i .).status =2) and (r(.x^.v^.i,y^.i .).macht>z) and (r(.x^.v^.i,y^.i .).geschlecht=2) THEN i:=i+1; 212 | IF (r(.x^.n^.i,y^.i .).status =2) and (r(.x^.n^.i,y^.i .).macht>z) and (r(.x^.n^.i,y^.i .).geschlecht=2) THEN i:=i+1; 213 | IF (r(.x^.v^.i,y^.n^.i.).status =2) and (r(.x^.v^.i,y^.n^.i.).macht>z) and (r(.x^.v^.i,y^.n^.i.).geschlecht=2) THEN i:=i+1; 214 | IF (r(.x^.i ,y^.n^.i.).status =2) and (r(.x^.i ,y^.n^.i.).macht>z) and (r(.x^.i ,y^.n^.i.).geschlecht=2) THEN i:=i+1; 215 | IF (r(.x^.n^.i,y^.n^.i.).status =2) and (r(.x^.n^.i,y^.n^.i.).macht>z) and (r(.x^.n^.i,y^.n^.i.).geschlecht=2) THEN i:=i+1; 216 | IF I=0 217 | THEN staerkeresmaennchen:= false 218 | ELSE staerkeresmaennchen:= true; 219 | END; 220 | 221 | BEGIN 222 | r(.x^.i,y^.i.).alter:=r(.x^.i,y^.i.).alter+1; 223 | neu:=r(.x^.i,y^.i.); 224 | 225 | IF r(.x^.i,y^.i.).status = 0 (* status leeres feld*) 226 | THEN 227 | BEGIN 228 | kind (r,x,y); 229 | END 230 | ELSE 231 | BEGIN 232 | IF umgebungleer(r,x,y) or (neu.alter>8) (* vereinzeltes individuum umgebung leer vereinsamt oder hoechtlebensalter errecht *) 233 | THEN 234 | BEGIN 235 | neu.status:=0; 236 | neu.alter:=0; 237 | neu.geschlecht:=0; 238 | neu.kultur:=0; 239 | neu.macht:=0; 240 | END 241 | ELSE 242 | BEGIN 243 | IF r(.x^.i,y^.i.).status = 1 (* jungtier weiblich männlich*) 244 | THEN 245 | BEGIN 246 | (* wenn umgebung männlicher silberrücken anderer kultur dann status auf leer*) 247 | IF umgebungmaennlichererwachsener(r,x,y) 248 | THEN 249 | BEGIN 250 | neu.status:=0; 251 | neu.alter:=0; 252 | neu.geschlecht:=0; 253 | neu.kultur:=0; 254 | neu.macht:=0; 255 | END 256 | ELSE 257 | BEGIN (* erfahrene Tiere *) 258 | (* wenn weiblich dann Kultur des silberrücken annehmen*) 259 | IF (r(.x^.i,y^.i.).geschlecht=1) 260 | THEN 261 | BEGIN 262 | neu.status:=2; 263 | neu.geschlecht:=1; 264 | neu.kultur:=kulturstaerkstesmaennchenannehmen (r,x,y); 265 | neu.macht:=r(.x^.i,y^.i.).macht; 266 | IF neu.kultur=0 267 | THEN 268 | BEGIN 269 | neu.status:=0; 270 | neu.alter:=0; 271 | neu.geschlecht:=0; 272 | neu.kultur:=0; 273 | neu.macht:=0; 274 | END 275 | END; 276 | 277 | (* wenn männliches tier *) 278 | IF (r(.x^.i,y^.i.).geschlecht=2) 279 | (* und stärkere männchen dann leer*) 280 | THEN 281 | BEGIN 282 | IF (staerkeresmaennchen (r,x,y)) 283 | THEN 284 | BEGIN 285 | neu.status:=0; 286 | neu.alter:=0; 287 | neu.geschlecht:=0; 288 | neu.kultur:=0; 289 | neu.macht:=0; 290 | END 291 | (* sonst randomisiere macht machtkampf*) 292 | ELSE 293 | BEGIN 294 | neu.status:=2; 295 | neu.geschlecht:=2; 296 | neu.kultur:=r(.x^.i,y^.i.).kultur; 297 | z:=random(15); 298 | IF z>9 THen z:=0; neu.macht:=z; 299 | END; 300 | IF neu.macht=0 301 | THEN 302 | BEGIN 303 | neu.status:=0; 304 | neu.alter:=0; 305 | neu.geschlecht:=0; 306 | neu.kultur:=0; 307 | neu.macht:=0; 308 | END 309 | END 310 | END 311 | END 312 | END 313 | END 314 | END; 315 | 316 | 317 | PROCEDURE zufall(VAR von:raum); 318 | VAR x,y,z:integer; 319 | BEGIN 320 | FOR y := 1 TO 24 321 | DO 322 | FOR x := 1 TO 80 323 | DO 324 | BEGIN 325 | von(.x,y.).status:=random(3); 326 | IF von(.x,y.).status=0 327 | THEN 328 | BEGIN 329 | von(.x,y.).geschlecht:=0; 330 | von(.x,y.).macht:=0; 331 | von(.x,y.).kultur:=0; 332 | END 333 | ELSE 334 | BEGIN 335 | von(.x,y.).geschlecht:=random(2)+1; 336 | von(.x,y.).macht:=random(9)+1; 337 | von(.x,y.).kultur:=random(9)+1; 338 | END; 339 | END; 340 | END; 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | end. 351 | 352 | -------------------------------------------------------------------------------- /primaten.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Primaten – erweiterte Kultursimulation mit GUI 4 | Eine agentenbasierte Simulation zur Erforschung kultureller Dynamiken in Primatengruppen 5 | """ 6 | 7 | import tkinter as tk 8 | from tkinter import ttk, messagebox, filedialog 9 | import numpy as np 10 | import random 11 | from datetime import datetime 12 | import csv 13 | from PIL import Image, ImageTk, ImageDraw 14 | import threading 15 | import time 16 | 17 | class Primat: 18 | """Klasse für einen einzelnen Primaten""" 19 | def __init__(self, status=0, alter=0, geschlecht=0, kultur=0, macht=0): 20 | self.status = status # 0=kein Primat, 1=jung, 2=erwachsen 21 | self.alter = alter # in Ticks 22 | self.geschlecht = geschlecht # 1=weiblich, 2=männlich 23 | self.kultur = kultur # 1-9 für verschiedene Kulturen 24 | self.macht = macht # Sozialer Einfluss (1-9) 25 | 26 | class PrimatenSimulation: 27 | """Hauptklasse für die Primaten-Simulation""" 28 | 29 | def __init__(self, breite=60, hoehe=60, initial_dichte=0.1): 30 | self.breite = breite 31 | self.hoehe = hoehe 32 | self.raum = np.empty((hoehe, breite), dtype=object) 33 | self.tick_index = 0 34 | self.history = [] 35 | self.max_history = 5000 36 | self.kultur_farben = [ 37 | None, '#e6194B', '#3cb44b', '#ffe119', '#4363d8', 38 | '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#bcf60c' 39 | ] 40 | self.initialisiere_raum(initial_dichte) 41 | 42 | def initialisiere_raum(self, dichte=0.1): 43 | """Initialisiert den Raum mit zufällig verteilten Primaten""" 44 | for y in range(self.hoehe): 45 | for x in range(self.breite): 46 | if random.random() < dichte: 47 | # Zufällige Eigenschaften für neuen Primaten 48 | geschlecht = 1 if random.random() < 0.5 else 2 49 | kultur = random.randint(1, 9) 50 | macht = random.randint(1, 9) 51 | self.raum[y][x] = Primat(1, 0, geschlecht, kultur, macht) 52 | else: 53 | self.raum[y][x] = Primat() 54 | 55 | self.tick_index = 0 56 | self.history = [] 57 | self.berechne_statistik() 58 | 59 | def nachbarn(self, x, y): 60 | """Gibt die 8 Nachbarn einer Position zurück (toroidale Geometrie)""" 61 | nachbarn_pos = [] 62 | for dy in [-1, 0, 1]: 63 | for dx in [-1, 0, 1]: 64 | if dx == 0 and dy == 0: 65 | continue 66 | nx = (x + dx) % self.breite 67 | ny = (y + dy) % self.hoehe 68 | nachbarn_pos.append(self.raum[ny][nx]) 69 | return nachbarn_pos 70 | 71 | def kind_erzeugen(self, mutter): 72 | """Erzeugt ein neues Kind basierend auf der Mutter""" 73 | geschlecht = 1 if random.random() < 0.5 else 2 74 | return Primat(1, 0, geschlecht, mutter.kultur, mutter.macht) 75 | 76 | def neue_generation(self, x, y): 77 | """Berechnet den neuen Zustand für eine Position""" 78 | aktuell = self.raum[y][x] 79 | nachbarn = self.nachbarn(x, y) 80 | 81 | # Kopie des aktuellen Zustands 82 | neu = Primat(aktuell.status, aktuell.alter, aktuell.geschlecht, 83 | aktuell.kultur, aktuell.macht) 84 | 85 | # Alterungsprozess 86 | if neu.status > 0: 87 | if random.random() < 0.8: 88 | neu.alter += 1 89 | if neu.alter > 19: 90 | neu.status = 0 # Tod 91 | elif neu.alter >= 3 and neu.status == 1: 92 | neu.status = 2 # Erwachsen werden 93 | 94 | # Geburt neuer Primaten 95 | if neu.status == 0: 96 | weibchen = [p for p in nachbarn if p.status == 2 and p.geschlecht == 1] 97 | maennchen = [p for p in nachbarn if p.status == 2 and p.geschlecht == 2] 98 | 99 | if weibchen and maennchen and random.random() < 0.25: 100 | mutter = random.choice(weibchen) 101 | return self.kind_erzeugen(mutter) 102 | 103 | # Spontane Entstehung (Migration) 104 | if random.random() < 0.001: 105 | geschlecht = 1 if random.random() < 0.5 else 2 106 | kultur = random.randint(1, 9) 107 | macht = random.randint(1, 9) 108 | return Primat(1, 0, geschlecht, kultur, macht) 109 | 110 | # Isolationstod - wenn komplett von anderen Kulturen umgeben 111 | if neu.status > 0: 112 | fremde = [p for p in nachbarn if p.kultur != neu.kultur] 113 | if len(fremde) == 8: # Alle Nachbarn sind fremd 114 | return Primat() 115 | 116 | # Kulturelle Beeinflussung 117 | if neu.status == 2: 118 | staerkere = [p for p in nachbarn if p.status == 2 and p.macht > neu.macht] 119 | if staerkere: 120 | einflussreichster = max(staerkere, key=lambda p: p.macht) 121 | if random.random() < 0.3: 122 | neu.kultur = einflussreichster.kultur 123 | neu.macht = max(0, neu.macht - 1) 124 | else: 125 | neu.macht = min(9, neu.macht + 0.1) 126 | 127 | return neu 128 | 129 | def tick(self): 130 | """Führt einen Simulationsschritt durch""" 131 | neuer_raum = np.empty((self.hoehe, self.breite), dtype=object) 132 | 133 | for y in range(self.hoehe): 134 | for x in range(self.breite): 135 | neuer_raum[y][x] = self.neue_generation(x, y) 136 | 137 | self.raum = neuer_raum 138 | self.tick_index += 1 139 | return self.berechne_statistik() 140 | 141 | def berechne_statistik(self): 142 | """Berechnet Statistiken über die aktuelle Population""" 143 | kultur_zaehler = [0] * 9 144 | gesamt_population = 0 145 | 146 | for y in range(self.hoehe): 147 | for x in range(self.breite): 148 | p = self.raum[y][x] 149 | if p.status > 0 and p.kultur > 0: 150 | kultur_zaehler[p.kultur - 1] += 1 151 | gesamt_population += 1 152 | 153 | # Anteile berechnen 154 | anteile = [count / gesamt_population if gesamt_population > 0 else 0 155 | for count in kultur_zaehler] 156 | 157 | # Zur Historie hinzufügen 158 | datenpunkt = { 159 | 'tick': self.tick_index, 160 | 'population': gesamt_population, 161 | 'anteile': anteile.copy(), 162 | 'kultur_counts': kultur_zaehler.copy() 163 | } 164 | 165 | self.history.append(datenpunkt) 166 | if len(self.history) > self.max_history: 167 | self.history.pop(0) 168 | 169 | return anteile, gesamt_population 170 | 171 | def monokultur_erkannt(self, anteile, population): 172 | """Prüft, ob eine Monokultur erreicht wurde""" 173 | if population < 10: 174 | return False, None 175 | 176 | max_anteil = max(anteile) 177 | if max_anteil >= 0.995: 178 | dominante_kultur = anteile.index(max_anteil) + 1 179 | return True, dominante_kultur 180 | 181 | return False, None 182 | 183 | def export_csv(self, dateiname=None): 184 | """Exportiert die Simulationsdaten als CSV""" 185 | if not dateiname: 186 | zeitstempel = datetime.now().strftime("%Y%m%d_%H%M%S") 187 | dateiname = f"kulturverlauf_{zeitstempel}.csv" 188 | 189 | with open(dateiname, 'w', newline='', encoding='utf-8') as csvfile: 190 | writer = csv.writer(csvfile) 191 | # Header schreiben 192 | header = ['tick', 'population'] + [f'kultur_{i+1}' for i in range(9)] 193 | writer.writerow(header) 194 | 195 | # Daten schreiben 196 | for eintrag in self.history: 197 | zeile = [eintrag['tick'], eintrag['population']] 198 | zeile.extend([f"{a:.5f}" for a in eintrag['anteile']]) 199 | writer.writerow(zeile) 200 | 201 | return dateiname 202 | 203 | class PrimatenGUI: 204 | """Grafische Benutzeroberfläche für die Primaten-Simulation""" 205 | 206 | def __init__(self, root): 207 | self.root = root 208 | self.root.title("Primaten – erweiterte Kultursimulation") 209 | self.root.geometry("1200x800") 210 | 211 | # Simulation initialisieren 212 | self.simulation = PrimatenSimulation(40, 40, 0.1) 213 | self.laufend = False 214 | self.tick_intervall = 150 # ms 215 | 216 | # GUI-Elemente erstellen 217 | self.erste_gui() 218 | self.aktualisiere_anzeige() 219 | 220 | def erste_gui(self): 221 | """Erstellt die grafische Benutzeroberfläche""" 222 | # Hauptframe 223 | main_frame = ttk.Frame(self.root, padding="10") 224 | main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) 225 | 226 | # Konfiguration der Grid-Gewichtung 227 | self.root.columnconfigure(0, weight=1) 228 | self.root.rowconfigure(0, weight=1) 229 | main_frame.columnconfigure(1, weight=1) 230 | main_frame.rowconfigure(1, weight=1) 231 | 232 | # Steuerungsbereich oben 233 | control_frame = ttk.LabelFrame(main_frame, text="Simulationssteuerung", padding="5") 234 | control_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10)) 235 | 236 | # Buttons 237 | btn_frame = ttk.Frame(control_frame) 238 | btn_frame.grid(row=0, column=0, sticky=tk.W) 239 | 240 | ttk.Button(btn_frame, text="🔀 Zufallsverteilung", 241 | command=self.zufallsverteilung).grid(row=0, column=0, padx=(0, 5)) 242 | ttk.Button(btn_frame, text="▶ Start", 243 | command=self.start_simulation, style="Accent.TButton").grid(row=0, column=1, padx=5) 244 | ttk.Button(btn_frame, text="⏸ Stopp", 245 | command=self.stopp_simulation).grid(row=0, column=2, padx=5) 246 | 247 | # Einstellungen 248 | settings_frame = ttk.Frame(control_frame) 249 | settings_frame.grid(row=0, column=1, sticky=tk.E) 250 | 251 | self.auto_stopp_var = tk.BooleanVar() 252 | ttk.Checkbutton(settings_frame, text="Auto-Stopp bei Monokultur", 253 | variable=self.auto_stopp_var).grid(row=0, column=0, padx=5) 254 | 255 | self.zeige_population_var = tk.BooleanVar(value=True) 256 | ttk.Checkbutton(settings_frame, text="Population anzeigen", 257 | variable=self.zeige_population_var).grid(row=0, column=1, padx=5) 258 | 259 | # Geschwindigkeits-Steuerung 260 | speed_frame = ttk.Frame(control_frame) 261 | speed_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(5, 0)) 262 | 263 | ttk.Label(speed_frame, text="Geschwindigkeit:").grid(row=0, column=0, padx=(0, 5)) 264 | self.speed_var = tk.StringVar(value="150") 265 | speed_combo = ttk.Combobox(speed_frame, textvariable=self.speed_var, 266 | values=["50", "100", "150", "250", "400"], 267 | state="readonly", width=8) 268 | speed_combo.grid(row=0, column=1, padx=5) 269 | speed_combo.bind('<<ComboboxSelected>>', self.geschwindigkeit_aendern) 270 | 271 | # Statistik-Anzeige 272 | stats_frame = ttk.Frame(control_frame) 273 | stats_frame.grid(row=1, column=2, sticky=tk.E) 274 | 275 | self.stats_label = ttk.Label(stats_frame, 276 | text="Tick: 0 | Population: 0 | Dominante Kultur: -") 277 | self.stats_label.grid(row=0, column=0) 278 | 279 | # Hauptanzeige-Bereich 280 | display_frame = ttk.Frame(main_frame) 281 | display_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S)) 282 | display_frame.columnconfigure(0, weight=1) 283 | display_frame.columnconfigure(1, weight=1) 284 | display_frame.rowconfigure(0, weight=1) 285 | 286 | # Linke Seite: Visualisierungen 287 | left_frame = ttk.Frame(display_frame) 288 | left_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 5)) 289 | left_frame.columnconfigure(0, weight=1) 290 | left_frame.rowconfigure(0, weight=1) 291 | left_frame.rowconfigure(1, weight=1) 292 | 293 | # Status-Anzeige 294 | status_frame = ttk.LabelFrame(left_frame, text="Status (Geschlecht & Alter)") 295 | status_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 5)) 296 | status_frame.columnconfigure(0, weight=1) 297 | status_frame.rowconfigure(0, weight=1) 298 | 299 | self.status_canvas = tk.Canvas(status_frame, width=320, height=320, bg="black") 300 | self.status_canvas.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) 301 | 302 | # Kultur-Anzeige 303 | kultur_frame = ttk.LabelFrame(left_frame, text="Kulturverteilung") 304 | kultur_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) 305 | kultur_frame.columnconfigure(0, weight=1) 306 | kultur_frame.rowconfigure(0, weight=1) 307 | 308 | self.kultur_canvas = tk.Canvas(kultur_frame, width=320, height=320, bg="black") 309 | self.kultur_canvas.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) 310 | 311 | # Rechte Seite: Diagramm und Legende 312 | right_frame = ttk.Frame(display_frame) 313 | right_frame.grid(row=0, column=1, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(5, 0)) 314 | right_frame.columnconfigure(0, weight=1) 315 | right_frame.rowconfigure(0, weight=3) 316 | right_frame.rowconfigure(1, weight=1) 317 | 318 | # Diagramm 319 | diagramm_frame = ttk.LabelFrame(right_frame, text="Kulturentwicklung") 320 | diagramm_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 5)) 321 | diagramm_frame.columnconfigure(0, weight=1) 322 | diagramm_frame.rowconfigure(0, weight=1) 323 | 324 | self.diagramm_canvas = tk.Canvas(diagramm_frame, width=400, height=300, bg="black") 325 | self.diagramm_canvas.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) 326 | 327 | # Legende und Export 328 | bottom_right_frame = ttk.Frame(right_frame) 329 | bottom_right_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) 330 | bottom_right_frame.columnconfigure(0, weight=1) 331 | bottom_right_frame.rowconfigure(0, weight=1) 332 | bottom_right_frame.rowconfigure(1, weight=0) 333 | 334 | # Legende 335 | legende_frame = ttk.LabelFrame(bottom_right_frame, text="Legende") 336 | legende_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) 337 | 338 | # Status-Legende 339 | status_legende_frame = ttk.Frame(legende_frame) 340 | status_legende_frame.grid(row=0, column=0, sticky=tk.W, padx=5, pady=5) 341 | 342 | ttk.Label(status_legende_frame, text="Status:").grid(row=0, column=0, columnspan=2, sticky=tk.W) 343 | 344 | farben_status = [ 345 | ("weiblich, jung", "#ffb6c1"), 346 | ("männlich, jung", "#87cefa"), 347 | ("weiblich, erwachsen", "#ff69b4"), 348 | ("männlich, erwachsen", "#1e90ff") 349 | ] 350 | 351 | for i, (text, farbe) in enumerate(farben_status): 352 | canvas = tk.Canvas(status_legende_frame, width=15, height=15, bg=farbe, highlightthickness=1) 353 | canvas.grid(row=i+1, column=0, padx=(0, 5), pady=2) 354 | ttk.Label(status_legende_frame, text=text).grid(row=i+1, column=1, sticky=tk.W, pady=2) 355 | 356 | # Kultur-Legende 357 | kultur_legende_frame = ttk.Frame(legende_frame) 358 | kultur_legende_frame.grid(row=0, column=1, sticky=tk.W, padx=5, pady=5) 359 | 360 | ttk.Label(kultur_legende_frame, text="Kulturen:").grid(row=0, column=0, columnspan=2, sticky=tk.W) 361 | 362 | for i in range(1, 10): 363 | row = (i-1) // 3 + 1 364 | col = (i-1) % 3 365 | farbe = self.simulation.kultur_farben[i] 366 | canvas = tk.Canvas(kultur_legende_frame, width=15, height=15, bg=farbe, highlightthickness=1) 367 | canvas.grid(row=row, column=col*2, padx=(5, 2), pady=2) 368 | ttk.Label(kultur_legende_frame, text=str(i)).grid(row=row, column=col*2+1, sticky=tk.W, pady=2) 369 | 370 | # Export-Buttons 371 | export_frame = ttk.Frame(bottom_right_frame) 372 | export_frame.grid(row=1, column=0, sticky=tk.E, pady=(5, 0)) 373 | 374 | ttk.Button(export_frame, text="💾 CSV Export", 375 | command=self.export_csv).grid(row=0, column=0, padx=5) 376 | ttk.Button(export_frame, text="🖼 PNG Export", 377 | command=self.export_png).grid(row=0, column=1, padx=5) 378 | 379 | def hex_to_rgb(self, hex_color): 380 | """Wandelt Hex-Farben in RGB um""" 381 | hex_color = hex_color.lstrip('#') 382 | return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4)) 383 | 384 | def zeichne_status(self): 385 | """Zeichnet die Status-Ansicht""" 386 | canvas = self.status_canvas 387 | canvas.delete("all") 388 | 389 | breite = canvas.winfo_width() 390 | hoehe = canvas.winfo_height() 391 | zell_groesse = min(breite // self.simulation.breite, hoehe // self.simulation.hoehe) 392 | 393 | farb_map = { 394 | (1, 1): "#ffb6c1", # weiblich, jung 395 | (1, 2): "#87cefa", # männlich, jung 396 | (2, 1): "#ff69b4", # weiblich, erwachsen 397 | (2, 2): "#1e90ff" # männlich, erwachsen 398 | } 399 | 400 | for y in range(self.simulation.hoehe): 401 | for x in range(self.simulation.breite): 402 | p = self.simulation.raum[y][x] 403 | if p.status > 0: 404 | farbe = farb_map.get((p.status, p.geschlecht), "white") 405 | x1 = x * zell_groesse 406 | y1 = y * zell_groesse 407 | x2 = x1 + zell_groesse 408 | y2 = y1 + zell_groesse 409 | canvas.create_rectangle(x1, y1, x2, y2, fill=farbe, outline="") 410 | 411 | def zeichne_kultur(self): 412 | """Zeichnet die Kultur-Ansicht""" 413 | canvas = self.kultur_canvas 414 | canvas.delete("all") 415 | 416 | breite = canvas.winfo_width() 417 | hoehe = canvas.winfo_height() 418 | zell_groesse = min(breite // self.simulation.breite, hoehe // self.simulation.hoehe) 419 | 420 | for y in range(self.simulation.hoehe): 421 | for x in range(self.simulation.breite): 422 | p = self.simulation.raum[y][x] 423 | if p.kultur > 0: 424 | farbe = self.simulation.kultur_farben[p.kultur] 425 | x1 = x * zell_groesse 426 | y1 = y * zell_groesse 427 | x2 = x1 + zell_groesse 428 | y2 = y1 + zell_groesse 429 | canvas.create_rectangle(x1, y1, x2, y2, fill=farbe, outline="") 430 | 431 | def zeichne_diagramm(self): 432 | """Zeichnet das Entwicklungsdiagramm""" 433 | canvas = self.diagramm_canvas 434 | canvas.delete("all") 435 | 436 | if len(self.simulation.history) < 2: 437 | return 438 | 439 | breite = canvas.winfo_width() 440 | hoehe = canvas.winfo_height() 441 | padding = 40 442 | 443 | # Fenstergröße für Diagramm 444 | fenster = min(len(self.simulation.history), 600) 445 | start_index = max(0, len(self.simulation.history) - fenster) 446 | daten = self.simulation.history[start_index:] 447 | 448 | if len(daten) < 2: 449 | return 450 | 451 | # Koordinaten berechnen 452 | ticks = [d['tick'] for d in daten] 453 | min_tick = min(ticks) 454 | max_tick = max(ticks) 455 | 456 | # Raster zeichnen 457 | canvas.create_rectangle(padding, padding, breite-padding, hoehe-padding, 458 | outline="#666666", fill="black") 459 | 460 | # Y-Achse beschriften 461 | for i in range(6): 462 | y = padding + i * (hoehe - 2*padding) / 5 463 | wert = 1.0 - i * 0.2 464 | canvas.create_text(padding - 10, y, text=f"{wert:.1f}", 465 | fill="white", anchor="e", font=("Arial", 8)) 466 | canvas.create_line(padding, y, breite-padding, y, fill="#333333") 467 | 468 | # X-Achse beschriften 469 | tick_step = max(1, (max_tick - min_tick) // 5) 470 | for i in range(0, len(daten), max(1, len(daten)//5)): 471 | if i < len(daten): 472 | x = padding + i * (breite - 2*padding) / len(daten) 473 | canvas.create_text(x, hoehe - padding + 15, text=str(daten[i]['tick']), 474 | fill="white", anchor="n", font=("Arial", 8)) 475 | 476 | # Kulturlinien zeichnen 477 | for k in range(9): 478 | farbe = self.simulation.kultur_farben[k + 1] 479 | punkte = [] 480 | 481 | for i, datenpunkt in enumerate(daten): 482 | x = padding + i * (breite - 2*padding) / len(daten) 483 | y = hoehe - padding - datenpunkt['anteile'][k] * (hoehe - 2*padding) 484 | punkte.extend([x, y]) 485 | 486 | if len(punkte) >= 4: 487 | canvas.create_line(punkte, fill=farbe, width=2, smooth=True) 488 | 489 | # Populationslinie zeichnen (falls aktiviert) 490 | if self.zeige_population_var.get(): 491 | max_pop = self.simulation.breite * self.simulation.hoehe 492 | punkte = [] 493 | 494 | for i, datenpunkt in enumerate(daten): 495 | x = padding + i * (breite - 2*padding) / len(daten) 496 | y = hoehe - padding - (datenpunkt['population'] / max_pop) * (hoehe - 2*padding) 497 | punkte.extend([x, y]) 498 | 499 | if len(punkte) >= 4: 500 | canvas.create_line(punkte, fill="white", width=1.5, dash=(4, 2)) 501 | 502 | def aktualisiere_statistik(self): 503 | """Aktualisiert die Statistik-Anzeige""" 504 | if self.simulation.history: 505 | aktuell = self.simulation.history[-1] 506 | anteile = aktuell['anteile'] 507 | dominante_kultur = anteile.index(max(anteile)) + 1 508 | 509 | text = f"Tick: {aktuell['tick']} | Population: {aktuell['population']} | Dominante Kultur: K{dominante_kultur}" 510 | 511 | # Monokultur-Prüfung 512 | mono, kultur = self.simulation.monokultur_erkannt(anteile, aktuell['population']) 513 | if mono: 514 | text += f" | MONOKULTUR: K{kultur}" 515 | if self.auto_stopp_var.get() and self.laufend: 516 | self.stopp_simulation() 517 | messagebox.showinfo("Monokultur erkannt", f"Monokultur erreicht: Kultur {kultur}") 518 | 519 | self.stats_label.config(text=text) 520 | 521 | def aktualisiere_anzeige(self): 522 | """Aktualisiert alle Anzeigen""" 523 | self.zeichne_status() 524 | self.zeichne_kultur() 525 | self.zeichne_diagramm() 526 | self.aktualisiere_statistik() 527 | 528 | def simulations_loop(self): 529 | """Haupt-Schleife für die Simulation""" 530 | if self.laufend: 531 | self.simulation.tick() 532 | self.aktualisiere_anzeige() 533 | self.root.after(self.tick_intervall, self.simulations_loop) 534 | 535 | def start_simulation(self): 536 | """Startet die Simulation""" 537 | if not self.laufend: 538 | self.laufend = True 539 | self.simulations_loop() 540 | 541 | def stopp_simulation(self): 542 | """Stoppt die Simulation""" 543 | self.laufend = False 544 | 545 | def zufallsverteilung(self): 546 | """Setzt eine neue Zufallsverteilung""" 547 | self.stopp_simulation() 548 | self.simulation.initialisiere_raum(0.1) 549 | self.aktualisiere_anzeige() 550 | 551 | def geschwindigkeit_aendern(self, event=None): 552 | """Ändert die Simulationsgeschwindigkeit""" 553 | self.tick_intervall = int(self.speed_var.get()) 554 | 555 | def export_csv(self): 556 | """Exportiert die Daten als CSV""" 557 | try: 558 | dateiname = filedialog.asksaveasfilename( 559 | defaultextension=".csv", 560 | filetypes=[("CSV Dateien", "*.csv"), ("Alle Dateien", "*.*")], 561 | title="Simulationsdaten speichern" 562 | ) 563 | if dateiname: 564 | export_datei = self.simulation.export_csv(dateiname) 565 | messagebox.showinfo("Export erfolgreich", f"Daten exportiert nach:\n{export_datei}") 566 | except Exception as e: 567 | messagebox.showerror("Export Fehler", f"Fehler beim Export: {str(e)}") 568 | 569 | def export_png(self): 570 | """Exportiert das Diagramm als PNG (vereinfacht)""" 571 | try: 572 | # Hier könnte man mit PIL ein echtes PNG erstellen 573 | # Für diese Version zeigen wir einfach eine Info an 574 | messagebox.showinfo("PNG Export", 575 | "PNG Export wird in dieser Version vereinfacht dargestellt.\n" 576 | "Die Daten sind im CSV-Export enthalten.") 577 | except Exception as e: 578 | messagebox.showerror("Export Fehler", f"Fehler beim PNG-Export: {str(e)}") 579 | 580 | def main(): 581 | """Hauptfunktion""" 582 | try: 583 | root = tk.Tk() 584 | app = PrimatenGUI(root) 585 | root.mainloop() 586 | except Exception as e: 587 | print(f"Fehler: {e}") 588 | input("Drücken Sie Enter zum Beenden...") 589 | 590 | if __name__ == "__main__": 591 | main() -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="de"> 3 | <head> 4 | <meta charset="UTF-8" /> 5 | <meta name="viewport" content="width=device-width, initial-scale=1" /> 6 | <title>Primaten – erweiterte Kultursimulation 7 | 223 | 224 | 225 | 226 |
227 | Headerbild 228 |
229 | 230 | 240 | 241 |
242 |

Primaten – erweiterte Kultursimulation

243 |

Eine agentenbasierte Simulation zur Erforschung kultureller Dynamiken in Primatengruppen

244 |

An agent-based simulation for exploring cultural dynamics in primate groups

245 | 246 |
247 |
248 |
249 | Primaten-Simulation 250 |

Primaten-Simulation

251 |

252 | Primaten-Simulation: Kulturelle Dynamiken in Sozialverbänden: 253 | Diese erweiterte Simulation modelliert kulturelle Übertragungsprozesse unter Ressourcenlimitierung. Jedes Individuum verfügt über multiple kulturelle Merkmale (kulturelle Hybridisierung). Männchen etablieren Dominanzhierarchien, die durch lokale Ressourcenverfügbarkeit moduliert werden, während Weibchen kulturelle Kontinuität durch assortative Paarung fördern. Das System untersucht die Bedingungen für friedliche Koexistenz versus kompetitive Verdrängung. 254 |

255 | 256 |

257 | Primate Simulation: Cultural Dynamics in Social Groups: 258 | This advanced simulation models cultural transmission processes under resource constraints. Each individual possesses multiple cultural traits (cultural hybridization). Males establish dominance hierarchies modulated by local resource availability, while females promote cultural continuity through assortative mating. The system examines conditions for peaceful coexistence versus competitive exclusion. 259 |

260 | 263 |
264 |
265 | 266 |
267 |
268 | PNG Export 269 |

PNG Export

270 |

271 | Die Simulation ermöglicht den Export der Diagramme als PNG-Bilder. Diese Funktion erlaubt es, den Verlauf der kulturellen Entwicklung visuell festzuhalten und für Präsentationen oder Analysen zu verwenden. Die Diagramme zeigen die Verteilung der Kulturen über die Zeit. 272 |

273 |

274 | The simulation allows exporting diagrams as PNG images. This function enables capturing the course of cultural development visually for use in presentations or analyses. The diagrams show the distribution of cultures over time. 275 |

276 | 279 |
280 |
281 | 282 |
283 |
284 | CSV Export 285 |

CSV Export

286 |

287 | Mit dem CSV-Export können die Simulationsdaten für weitergehende statistische Analysen gespeichert werden. Die exportierten Daten enthalten Informationen zur Populationsgröße und zur Verteilung der Kulturen zu jedem Zeitpunkt der Simulation. 288 |

289 |

290 | With the CSV export, simulation data can be saved for further statistical analyses. The exported data contains information on population size and the distribution of cultures at each point in time of the simulation. 291 |

292 | 295 |
296 |
297 |
298 |
299 | 300 | 301 |
302 |

Primaten-Kultursimulation

303 | 304 |
305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 321 | 322 | 327 | 328 |
329 |
330 | 331 |
332 |
Population:
333 |
Tick: 0
334 |
Dominante Kultur:
335 |
336 | 337 |
338 |
339 |
340 |
341 | 342 | 343 |
344 |
345 | 346 |
347 |
348 | 349 | 350 |
351 |
352 |
353 | 354 |
355 |
356 |
Legende — Geschlecht & Alter
357 |
358 |
weiblich, jung
359 |
männlich, jung
360 |
weiblich, erwachsen
361 |
männlich, erwachsen
362 |
363 |
364 |
Kulturfarben
365 |
366 |
367 |
368 |
369 |
370 | 371 | 380 | 381 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | -------------------------------------------------------------------------------- /primatenOpt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Primaten – erweiterte Kultursimulation mit GUI 4 | Eine agentenbasierte Simulation zur Erforschung kultureller Dynamiken in Primatengruppen 5 | Optimierte Version mit 5 Erweiterungen: 6 | 1. Kulturelle Hybridisierung 7 | 2. Macht-Puffer für Kulturwechsel 8 | 3. Weibliche Affinität bei Partnerwahl 9 | 4. Ressourcen-System 10 | 5. Kulturelle Toleranz 11 | """ 12 | 13 | import tkinter as tk 14 | from tkinter import ttk, messagebox, filedialog 15 | import numpy as np 16 | import random 17 | from datetime import datetime 18 | import csv 19 | from PIL import Image, ImageTk, ImageDraw 20 | import threading 21 | import time 22 | 23 | class Primat: 24 | """Klasse für einen einzelnen Primaten mit erweiterten Eigenschaften""" 25 | def __init__(self, status=0, alter=0, geschlecht=0, kultur=0, kultur2=0, macht=0): 26 | self.status = status # 0=kein Primat, 1=jung, 2=erwachsen 27 | self.alter = alter # in Ticks 28 | self.geschlecht = geschlecht # 1=weiblich, 2=männlich 29 | self.kultur = kultur # Primärkultur (1-9) 30 | self.kultur2 = kultur2 # Sekundärkultur - NEU: Hybridisierung 31 | self.macht = macht # Sozialer Einfluss (1-9) 32 | 33 | class PrimatenSimulation: 34 | """Hauptklasse für die Primaten-Simulation mit 5 Erweiterungen""" 35 | 36 | def __init__(self, breite=40, hoehe=40, initial_dichte=0.1): 37 | self.breite = breite 38 | self.hoehe = hoehe 39 | self.raum = np.empty((hoehe, breite), dtype=object) 40 | self.ressourcen = np.zeros((hoehe, breite), dtype=int) # NEU: Ressourcen-Ebene 41 | self.tick_index = 0 42 | self.history = [] 43 | self.max_history = 5000 44 | self.kultur_farben = [ 45 | None, '#e6194B', '#3cb44b', '#ffe119', '#4363d8', 46 | '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#bcf60c' 47 | ] 48 | # NEU: Kulturelle Toleranz-Werte 49 | self.kultur_toleranz = [None, 0.2, 0.8, 0.4, 0.9, 0.5, 0.1, 0.7, 0.3, 0.6] 50 | 51 | self.initialisiere_raum(initial_dichte) 52 | self.initialisiere_ressourcen() # NEU: Ressourcen initialisieren 53 | 54 | def initialisiere_ressourcen(self): 55 | """Initialisiert die Ressourcen-Ebene""" 56 | for y in range(self.hoehe): 57 | for x in range(self.breite): 58 | self.ressourcen[y][x] = random.randint(0, 5) # 0-5 Ressourcen pro Zelle 59 | 60 | def initialisiere_raum(self, dichte=0.1): 61 | """Initialisiert den Raum mit zufällig verteilten Primaten""" 62 | for y in range(self.hoehe): 63 | for x in range(self.breite): 64 | if random.random() < dichte: 65 | # Zufällige Eigenschaften für neuen Primaten 66 | geschlecht = 1 if random.random() < 0.5 else 2 67 | kultur = random.randint(1, 9) 68 | kultur2 = random.randint(1, 9) # NEU: Sekundärkultur 69 | macht = random.randint(1, 9) 70 | self.raum[y][x] = Primat(1, 0, geschlecht, kultur, kultur2, macht) 71 | else: 72 | self.raum[y][x] = Primat() 73 | 74 | self.tick_index = 0 75 | self.history = [] 76 | self.berechne_statistik() 77 | 78 | def nachbarn(self, x, y): 79 | """Gibt die 8 Nachbarn einer Position zurück (toroidale Geometrie)""" 80 | nachbarn_pos = [] 81 | for dy in [-1, 0, 1]: 82 | for dx in [-1, 0, 1]: 83 | if dx == 0 and dy == 0: 84 | continue 85 | nx = (x + dx) % self.breite 86 | ny = (y + dy) % self.hoehe 87 | nachbarn_pos.append((self.raum[ny][nx], nx, ny)) 88 | return nachbarn_pos 89 | 90 | def kulturelle_naehe(self, p1, p2): 91 | """NEU: Berechnet kulturelle Ähnlichkeit zwischen zwei Primaten""" 92 | if (p1.kultur == p2.kultur or p1.kultur == p2.kultur2 or 93 | p1.kultur2 == p2.kultur or p1.kultur2 == p2.kultur2): 94 | return 1 95 | return 0 96 | 97 | def kind_erzeugen(self, mutter, vater): 98 | """NEU: Erweitert - Kind erbt Primärkultur von Mutter, Sekundärkultur von Vater""" 99 | geschlecht = 1 if random.random() < 0.5 else 2 100 | return Primat(1, 0, geschlecht, mutter.kultur, vater.kultur, vater.macht) 101 | 102 | def get_nachbar_ressourcen(self, x, y): 103 | """NEU: Berechnet lokale Ressourcen-Konzentration""" 104 | sum_r = self.ressourcen[y][x] 105 | nachbarn = self.nachbarn(x, y) 106 | for nb, nx, ny in nachbarn: 107 | sum_r += self.ressourcen[ny][nx] 108 | return min(10, sum_r // 5) # Normiert auf 0-10 109 | 110 | def check_isolation(self, primat, x, y): 111 | """NEU: Prüft kulturelle Isolation unter Berücksichtigung der Toleranz""" 112 | if primat.status <= 0 or primat.kultur <= 0: 113 | return False 114 | 115 | nachbarn = self.nachbarn(x, y) 116 | fremde_nachbarn = 0 117 | 118 | for nb, nx, ny in nachbarn: 119 | if nb.status > 0 and nb.kultur > 0: 120 | if not self.kulturelle_naehe(primat, nb): 121 | fremde_nachbarn += 1 122 | 123 | isolationsgrad = fremde_nachbarn / 8.0 124 | toleranz = self.kultur_toleranz[primat.kultur] 125 | sterbewahrscheinlichkeit = isolationsgrad * (1 - toleranz) 126 | 127 | return random.random() < sterbewahrscheinlichkeit 128 | 129 | def neue_generation(self, x, y): 130 | """Berechnet den neuen Zustand für eine Position mit allen 5 Erweiterungen""" 131 | aktuell = self.raum[y][x] 132 | nachbarn = self.nachbarn(x, y) 133 | nachbarn_primaten = [nb for nb, nx, ny in nachbarn] 134 | 135 | # Kopie des aktuellen Zustands 136 | neu = Primat(aktuell.status, aktuell.alter, aktuell.geschlecht, 137 | aktuell.kultur, aktuell.kultur2, aktuell.macht) 138 | 139 | # NEU: Ressourcen-Verbrauch und Regeneration 140 | if neu.status > 0 and self.ressourcen[y][x] > 0: 141 | self.ressourcen[y][x] -= 1 142 | else: 143 | self.ressourcen[y][x] = min(5, self.ressourcen[y][x] + 1) 144 | 145 | # Alterungsprozess 146 | if neu.status > 0: 147 | if random.random() < 0.8: 148 | neu.alter += 1 149 | if neu.alter > 19: 150 | neu.status = 0 # Tod 151 | elif neu.alter >= 3 and neu.status == 1: 152 | neu.status = 2 # Erwachsen werden 153 | 154 | # NEU: Kulturelle Isolation (Tod durch fehlende Toleranz) 155 | if neu.status > 0 and self.check_isolation(neu, x, y): 156 | return Primat() 157 | 158 | # Geburt neuer Primaten 159 | if neu.status == 0: 160 | weibchen = [p for p in nachbarn_primaten if p.status == 2 and p.geschlecht == 1] 161 | maennchen = [p for p in nachbarn_primaten if p.status == 2 and p.geschlecht == 2] 162 | 163 | # NEU: Weibliche Affinität bei Partnerwahl 164 | if weibchen and maennchen and random.random() < 0.25: 165 | # Finde bestes Paar basierend auf Macht und kultureller Nähe 166 | bestes_paar = None 167 | bester_score = -1 168 | 169 | for w in weibchen: 170 | for m in maennchen: 171 | affinitaet = self.kulturelle_naehe(w, m) 172 | score = 0.8 * m.macht + 0.2 * affinitaet * 9 173 | if score > bester_score: 174 | bester_score = score 175 | bestes_paar = (w, m) 176 | 177 | if bestes_paar: 178 | return self.kind_erzeugen(bestes_paar[0], bestes_paar[1]) 179 | 180 | # Spontane Entstehung (Migration) 181 | if random.random() < 0.0005: 182 | geschlecht = 1 if random.random() < 0.5 else 2 183 | kultur = random.randint(1, 9) 184 | kultur2 = random.randint(1, 9) 185 | macht = random.randint(1, 9) 186 | return Primat(1, 0, geschlecht, kultur, kultur2, macht) 187 | 188 | # Kulturelle Beeinflussung (nur erwachsene Männchen) 189 | if neu.status == 2 and neu.geschlecht == 2: 190 | # NEU: Ressourcen-Bonus für Machtgewinn 191 | res_bonus = self.get_nachbar_ressourcen(x, y) / 10.0 192 | 193 | staerkere = [p for p in nachbarn_primaten 194 | if p.status == 2 and p.geschlecht == 2 and p.macht > neu.macht] 195 | 196 | if staerkere: 197 | einflussreichster = max(staerkere, key=lambda p: p.macht) 198 | 199 | # NEU: Macht-Puffer - nur bei großem Unterschied 200 | if einflussreichster.macht > neu.macht + 3 and random.random() < 0.3: 201 | neu.kultur = einflussreichster.kultur 202 | neu.kultur2 = einflussreichster.kultur2 203 | neu.macht = max(1, neu.macht - 1) 204 | else: 205 | # NEU: Machtgewinn durch Ressourcen 206 | neu.macht = min(9, neu.macht + 0.1 + res_bonus * 0.2) 207 | 208 | return neu 209 | 210 | def tick(self): 211 | """Führt einen Simulationsschritt durch""" 212 | neuer_raum = np.empty((self.hoehe, self.breite), dtype=object) 213 | 214 | for y in range(self.hoehe): 215 | for x in range(self.breite): 216 | neuer_raum[y][x] = self.neue_generation(x, y) 217 | 218 | self.raum = neuer_raum 219 | self.tick_index += 1 220 | return self.berechne_statistik() 221 | 222 | def berechne_statistik(self): 223 | """Berechnet Statistiken über die aktuelle Population""" 224 | kultur_zaehler = [0] * 9 225 | gesamt_population = 0 226 | 227 | for y in range(self.hoehe): 228 | for x in range(self.breite): 229 | p = self.raum[y][x] 230 | if p.status > 0 and p.kultur > 0: 231 | # Zähle Primärkultur 232 | kultur_zaehler[p.kultur - 1] += 1 233 | # NEU: Zähle Sekundärkultur wenn verschieden 234 | if p.kultur2 > 0 and p.kultur2 != p.kultur: 235 | kultur_zaehler[p.kultur2 - 1] += 1 236 | gesamt_population += 1 237 | 238 | # Anteile berechnen (kann jetzt >1 sein wegen doppelter Zählung) 239 | sum_kulturen = sum(kultur_zaehler) 240 | anteile = [count / sum_kulturen if sum_kulturen > 0 else 0 241 | for count in kultur_zaehler] 242 | 243 | # Zur Historie hinzufügen 244 | datenpunkt = { 245 | 'tick': self.tick_index, 246 | 'population': gesamt_population, 247 | 'anteile': anteile.copy(), 248 | 'kultur_counts': kultur_zaehler.copy() 249 | } 250 | 251 | self.history.append(datenpunkt) 252 | if len(self.history) > self.max_history: 253 | self.history.pop(0) 254 | 255 | return anteile, gesamt_population 256 | 257 | def monokultur_erkannt(self, anteile, population): 258 | """Prüft, ob eine Monokultur erreicht wurde""" 259 | if population < 10: 260 | return False, None 261 | 262 | max_anteil = max(anteile) 263 | if max_anteil >= 0.995: 264 | dominante_kultur = anteile.index(max_anteil) + 1 265 | return True, dominante_kultur 266 | 267 | return False, None 268 | 269 | def export_csv(self, dateiname=None): 270 | """Exportiert die Simulationsdaten als CSV""" 271 | if not dateiname: 272 | zeitstempel = datetime.now().strftime("%Y%m%d_%H%M%S") 273 | dateiname = f"kulturverlauf_{zeitstempel}.csv" 274 | 275 | with open(dateiname, 'w', newline='', encoding='utf-8') as csvfile: 276 | writer = csv.writer(csvfile) 277 | # Header schreiben 278 | header = ['tick', 'population'] + [f'kultur_{i+1}' for i in range(9)] 279 | writer.writerow(header) 280 | 281 | # Daten schreiben 282 | for eintrag in self.history: 283 | zeile = [eintrag['tick'], eintrag['population']] 284 | zeile.extend([f"{a:.5f}" for a in eintrag['anteile']]) 285 | writer.writerow(zeile) 286 | 287 | return dateiname 288 | 289 | class PrimatenGUI: 290 | """Grafische Benutzeroberfläche für die Primaten-Simulation""" 291 | 292 | def __init__(self, root): 293 | self.root = root 294 | self.root.title("Primaten – erweiterte Kultursimulation (Optimiert)") 295 | self.root.geometry("1200x800") 296 | 297 | # Simulation initialisieren 298 | self.simulation = PrimatenSimulation(40, 40, 0.1) 299 | self.laufend = False 300 | self.tick_intervall = 150 # ms 301 | 302 | # GUI-Elemente erstellen 303 | self.erste_gui() 304 | self.aktualisiere_anzeige() 305 | 306 | def erste_gui(self): 307 | """Erstellt die grafische Benutzeroberfläche""" 308 | # Hauptframe 309 | main_frame = ttk.Frame(self.root, padding="10") 310 | main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) 311 | 312 | # Konfiguration der Grid-Gewichtung 313 | self.root.columnconfigure(0, weight=1) 314 | self.root.rowconfigure(0, weight=1) 315 | main_frame.columnconfigure(1, weight=1) 316 | main_frame.rowconfigure(1, weight=1) 317 | 318 | # Steuerungsbereich oben 319 | control_frame = ttk.LabelFrame(main_frame, text="Simulationssteuerung", padding="5") 320 | control_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10)) 321 | 322 | # Buttons 323 | btn_frame = ttk.Frame(control_frame) 324 | btn_frame.grid(row=0, column=0, sticky=tk.W) 325 | 326 | ttk.Button(btn_frame, text="🔀 Zufallsverteilung", 327 | command=self.zufallsverteilung).grid(row=0, column=0, padx=(0, 5)) 328 | ttk.Button(btn_frame, text="▶ Start", 329 | command=self.start_simulation, style="Accent.TButton").grid(row=0, column=1, padx=5) 330 | ttk.Button(btn_frame, text="⏸ Stopp", 331 | command=self.stopp_simulation).grid(row=0, column=2, padx=5) 332 | 333 | # Einstellungen 334 | settings_frame = ttk.Frame(control_frame) 335 | settings_frame.grid(row=0, column=1, sticky=tk.E) 336 | 337 | self.auto_stopp_var = tk.BooleanVar() 338 | ttk.Checkbutton(settings_frame, text="Auto-Stopp bei Monokultur", 339 | variable=self.auto_stopp_var).grid(row=0, column=0, padx=5) 340 | 341 | self.zeige_population_var = tk.BooleanVar(value=True) 342 | ttk.Checkbutton(settings_frame, text="Population anzeigen", 343 | variable=self.zeige_population_var).grid(row=0, column=1, padx=5) 344 | 345 | # Geschwindigkeits-Steuerung 346 | speed_frame = ttk.Frame(control_frame) 347 | speed_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(5, 0)) 348 | 349 | ttk.Label(speed_frame, text="Geschwindigkeit:").grid(row=0, column=0, padx=(0, 5)) 350 | self.speed_var = tk.StringVar(value="150") 351 | speed_combo = ttk.Combobox(speed_frame, textvariable=self.speed_var, 352 | values=["50", "100", "150", "250", "400"], 353 | state="readonly", width=8) 354 | speed_combo.grid(row=0, column=1, padx=5) 355 | speed_combo.bind('<>', self.geschwindigkeit_aendern) 356 | 357 | # Statistik-Anzeige 358 | stats_frame = ttk.Frame(control_frame) 359 | stats_frame.grid(row=1, column=2, sticky=tk.E) 360 | 361 | self.stats_label = ttk.Label(stats_frame, 362 | text="Tick: 0 | Population: 0 | Dominante Kultur: -") 363 | self.stats_label.grid(row=0, column=0) 364 | 365 | # Hauptanzeige-Bereich 366 | display_frame = ttk.Frame(main_frame) 367 | display_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S)) 368 | display_frame.columnconfigure(0, weight=1) 369 | display_frame.columnconfigure(1, weight=1) 370 | display_frame.rowconfigure(0, weight=1) 371 | 372 | # Linke Seite: Visualisierungen 373 | left_frame = ttk.Frame(display_frame) 374 | left_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 5)) 375 | left_frame.columnconfigure(0, weight=1) 376 | left_frame.rowconfigure(0, weight=1) 377 | left_frame.rowconfigure(1, weight=1) 378 | 379 | # Status-Anzeige 380 | status_frame = ttk.LabelFrame(left_frame, text="Status (Geschlecht & Alter)") 381 | status_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 5)) 382 | status_frame.columnconfigure(0, weight=1) 383 | status_frame.rowconfigure(0, weight=1) 384 | 385 | self.status_canvas = tk.Canvas(status_frame, width=320, height=320, bg="black") 386 | self.status_canvas.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) 387 | 388 | # Kultur-Anzeige 389 | kultur_frame = ttk.LabelFrame(left_frame, text="Kulturverteilung") 390 | kultur_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) 391 | kultur_frame.columnconfigure(0, weight=1) 392 | kultur_frame.rowconfigure(0, weight=1) 393 | 394 | self.kultur_canvas = tk.Canvas(kultur_frame, width=320, height=320, bg="black") 395 | self.kultur_canvas.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) 396 | 397 | # Rechte Seite: Diagramm und Legende 398 | right_frame = ttk.Frame(display_frame) 399 | right_frame.grid(row=0, column=1, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(5, 0)) 400 | right_frame.columnconfigure(0, weight=1) 401 | right_frame.rowconfigure(0, weight=3) 402 | right_frame.rowconfigure(1, weight=1) 403 | 404 | # Diagramm 405 | diagramm_frame = ttk.LabelFrame(right_frame, text="Kulturentwicklung") 406 | diagramm_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 5)) 407 | diagramm_frame.columnconfigure(0, weight=1) 408 | diagramm_frame.rowconfigure(0, weight=1) 409 | 410 | self.diagramm_canvas = tk.Canvas(diagramm_frame, width=400, height=300, bg="black") 411 | self.diagramm_canvas.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) 412 | 413 | # Legende und Export 414 | bottom_right_frame = ttk.Frame(right_frame) 415 | bottom_right_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) 416 | bottom_right_frame.columnconfigure(0, weight=1) 417 | bottom_right_frame.rowconfigure(0, weight=1) 418 | bottom_right_frame.rowconfigure(1, weight=0) 419 | 420 | # Legende 421 | legende_frame = ttk.LabelFrame(bottom_right_frame, text="Legende") 422 | legende_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) 423 | 424 | # Status-Legende 425 | status_legende_frame = ttk.Frame(legende_frame) 426 | status_legende_frame.grid(row=0, column=0, sticky=tk.W, padx=5, pady=5) 427 | 428 | ttk.Label(status_legende_frame, text="Status:").grid(row=0, column=0, columnspan=2, sticky=tk.W) 429 | 430 | farben_status = [ 431 | ("weiblich, jung", "#ffb6c1"), 432 | ("männlich, jung", "#87cefa"), 433 | ("weiblich, erwachsen", "#ff69b4"), 434 | ("männlich, erwachsen", "#1e90ff") 435 | ] 436 | 437 | for i, (text, farbe) in enumerate(farben_status): 438 | canvas = tk.Canvas(status_legende_frame, width=15, height=15, bg=farbe, highlightthickness=1) 439 | canvas.grid(row=i+1, column=0, padx=(0, 5), pady=2) 440 | ttk.Label(status_legende_frame, text=text).grid(row=i+1, column=1, sticky=tk.W, pady=2) 441 | 442 | # Kultur-Legende 443 | kultur_legende_frame = ttk.Frame(legende_frame) 444 | kultur_legende_frame.grid(row=0, column=1, sticky=tk.W, padx=5, pady=5) 445 | 446 | ttk.Label(kultur_legende_frame, text="Kulturen:").grid(row=0, column=0, columnspan=2, sticky=tk.W) 447 | 448 | for i in range(1, 10): 449 | row = (i-1) // 3 + 1 450 | col = (i-1) % 3 451 | farbe = self.simulation.kultur_farben[i] 452 | canvas = tk.Canvas(kultur_legende_frame, width=15, height=15, bg=farbe, highlightthickness=1) 453 | canvas.grid(row=row, column=col*2, padx=(5, 2), pady=2) 454 | ttk.Label(kultur_legende_frame, text=str(i)).grid(row=row, column=col*2+1, sticky=tk.W, pady=2) 455 | 456 | # Export-Buttons 457 | export_frame = ttk.Frame(bottom_right_frame) 458 | export_frame.grid(row=1, column=0, sticky=tk.E, pady=(5, 0)) 459 | 460 | ttk.Button(export_frame, text="💾 CSV Export", 461 | command=self.export_csv).grid(row=0, column=0, padx=5) 462 | ttk.Button(export_frame, text="🖼 PNG Export", 463 | command=self.export_png).grid(row=0, column=1, padx=5) 464 | 465 | def zeichne_status(self): 466 | """Zeichnet die Status-Ansicht mit Ressourcen-Hintergrund""" 467 | canvas = self.status_canvas 468 | canvas.delete("all") 469 | 470 | breite = canvas.winfo_width() 471 | hoehe = canvas.winfo_height() 472 | zell_groesse = min(breite // self.simulation.breite, hoehe // self.simulation.hoehe) 473 | 474 | # NEU: Ressourcen als Hintergrund zeichnen 475 | for y in range(self.simulation.hoehe): 476 | for x in range(self.simulation.breite): 477 | ress = self.simulation.ressourcen[y][x] / 5.0 # 0-1 normalisiert 478 | gruen_wert = int(ress * 120) 479 | farbe = f"#00{gruen_wert:02x}00" 480 | x1 = x * zell_groesse 481 | y1 = y * zell_groesse 482 | x2 = x1 + zell_groesse 483 | y2 = y1 + zell_groesse 484 | canvas.create_rectangle(x1, y1, x2, y2, fill=farbe, outline="") 485 | 486 | farb_map = { 487 | (1, 1): "#ffb6c1", # weiblich, jung 488 | (1, 2): "#87cefa", # männlich, jung 489 | (2, 1): "#ff69b4", # weiblich, erwachsen 490 | (2, 2): "#1e90ff" # männlich, erwachsen 491 | } 492 | 493 | for y in range(self.simulation.hoehe): 494 | for x in range(self.simulation.breite): 495 | p = self.simulation.raum[y][x] 496 | if p.status > 0: 497 | farbe = farb_map.get((p.status, p.geschlecht), "white") 498 | x1 = x * zell_groesse 499 | y1 = y * zell_groesse 500 | x2 = x1 + zell_groesse 501 | y2 = y1 + zell_groesse 502 | canvas.create_rectangle(x1, y1, x2, y2, fill=farbe, outline="") 503 | 504 | def zeichne_kultur(self): 505 | """Zeichnet die Kultur-Ansicht""" 506 | canvas = self.kultur_canvas 507 | canvas.delete("all") 508 | 509 | breite = canvas.winfo_width() 510 | hoehe = canvas.winfo_height() 511 | zell_groesse = min(breite // self.simulation.breite, hoehe // self.simulation.hoehe) 512 | 513 | for y in range(self.simulation.hoehe): 514 | for x in range(self.simulation.breite): 515 | p = self.simulation.raum[y][x] 516 | if p.kultur > 0: 517 | farbe = self.simulation.kultur_farben[p.kultur] 518 | x1 = x * zell_groesse 519 | y1 = y * zell_groesse 520 | x2 = x1 + zell_groesse 521 | y2 = y1 + zell_groesse 522 | canvas.create_rectangle(x1, y1, x2, y2, fill=farbe, outline="") 523 | 524 | def zeichne_diagramm(self): 525 | """Zeichnet das Entwicklungsdiagramm""" 526 | canvas = self.diagramm_canvas 527 | canvas.delete("all") 528 | 529 | if len(self.simulation.history) < 2: 530 | return 531 | 532 | breite = canvas.winfo_width() 533 | hoehe = canvas.winfo_height() 534 | padding = 40 535 | 536 | # Fenstergröße für Diagramm 537 | fenster = min(len(self.simulation.history), 600) 538 | start_index = max(0, len(self.simulation.history) - fenster) 539 | daten = self.simulation.history[start_index:] 540 | 541 | if len(daten) < 2: 542 | return 543 | 544 | # Koordinaten berechnen 545 | ticks = [d['tick'] for d in daten] 546 | min_tick = min(ticks) 547 | max_tick = max(ticks) 548 | 549 | # Raster zeichnen 550 | canvas.create_rectangle(padding, padding, breite-padding, hoehe-padding, 551 | outline="#666666", fill="black") 552 | 553 | # Y-Achse beschriften 554 | for i in range(6): 555 | y = padding + i * (hoehe - 2*padding) / 5 556 | wert = 1.0 - i * 0.2 557 | canvas.create_text(padding - 10, y, text=f"{wert:.1f}", 558 | fill="white", anchor="e", font=("Arial", 8)) 559 | canvas.create_line(padding, y, breite-padding, y, fill="#333333") 560 | 561 | # X-Achse beschriften 562 | tick_step = max(1, (max_tick - min_tick) // 5) 563 | for i in range(0, len(daten), max(1, len(daten)//5)): 564 | if i < len(daten): 565 | x = padding + i * (breite - 2*padding) / len(daten) 566 | canvas.create_text(x, hoehe - padding + 15, text=str(daten[i]['tick']), 567 | fill="white", anchor="n", font=("Arial", 8)) 568 | 569 | # Kulturlinien zeichnen 570 | for k in range(9): 571 | farbe = self.simulation.kultur_farben[k + 1] 572 | punkte = [] 573 | 574 | for i, datenpunkt in enumerate(daten): 575 | x = padding + i * (breite - 2*padding) / len(daten) 576 | y = hoehe - padding - datenpunkt['anteile'][k] * (hoehe - 2*padding) 577 | punkte.extend([x, y]) 578 | 579 | if len(punkte) >= 4: 580 | canvas.create_line(punkte, fill=farbe, width=2, smooth=True) 581 | 582 | # Populationslinie zeichnen (falls aktiviert) 583 | if self.zeige_population_var.get(): 584 | max_pop = self.simulation.breite * self.simulation.hoehe 585 | punkte = [] 586 | 587 | for i, datenpunkt in enumerate(daten): 588 | x = padding + i * (breite - 2*padding) / len(daten) 589 | y = hoehe - padding - (datenpunkt['population'] / max_pop) * (hoehe - 2*padding) 590 | punkte.extend([x, y]) 591 | 592 | if len(punkte) >= 4: 593 | canvas.create_line(punkte, fill="white", width=1.5, dash=(4, 2)) 594 | 595 | def aktualisiere_statistik(self): 596 | """Aktualisiert die Statistik-Anzeige""" 597 | if self.simulation.history: 598 | aktuell = self.simulation.history[-1] 599 | anteile = aktuell['anteile'] 600 | dominante_kultur = anteile.index(max(anteile)) + 1 601 | 602 | text = f"Tick: {aktuell['tick']} | Population: {aktuell['population']} | Dominante Kultur: K{dominante_kultur}" 603 | 604 | # Monokultur-Prüfung 605 | mono, kultur = self.simulation.monokultur_erkannt(anteile, aktuell['population']) 606 | if mono: 607 | text += f" | MONOKULTUR: K{kultur}" 608 | if self.auto_stopp_var.get() and self.laufend: 609 | self.stopp_simulation() 610 | messagebox.showinfo("Monokultur erkannt", f"Monokultur erreicht: Kultur {kultur}") 611 | 612 | self.stats_label.config(text=text) 613 | 614 | def aktualisiere_anzeige(self): 615 | """Aktualisiert alle Anzeigen""" 616 | self.zeichne_status() 617 | self.zeichne_kultur() 618 | self.zeichne_diagramm() 619 | self.aktualisiere_statistik() 620 | 621 | def simulations_loop(self): 622 | """Haupt-Schleife für die Simulation""" 623 | if self.laufend: 624 | self.simulation.tick() 625 | self.aktualisiere_anzeige() 626 | self.root.after(self.tick_intervall, self.simulations_loop) 627 | 628 | def start_simulation(self): 629 | """Startet die Simulation""" 630 | if not self.laufend: 631 | self.laufend = True 632 | self.simulations_loop() 633 | 634 | def stopp_simulation(self): 635 | """Stoppt die Simulation""" 636 | self.laufend = False 637 | 638 | def zufallsverteilung(self): 639 | """Setzt eine neue Zufallsverteilung""" 640 | self.stopp_simulation() 641 | self.simulation.initialisiere_raum(0.1) 642 | self.simulation.initialisiere_ressourcen() # NEU: Ressourcen auch neu initialisieren 643 | self.aktualisiere_anzeige() 644 | 645 | def geschwindigkeit_aendern(self, event=None): 646 | """Ändert die Simulationsgeschwindigkeit""" 647 | self.tick_intervall = int(self.speed_var.get()) 648 | 649 | def export_csv(self): 650 | """Exportiert die Daten als CSV""" 651 | try: 652 | dateiname = filedialog.asksaveasfilename( 653 | defaultextension=".csv", 654 | filetypes=[("CSV Dateien", "*.csv"), ("Alle Dateien", "*.*")], 655 | title="Simulationsdaten speichern" 656 | ) 657 | if dateiname: 658 | export_datei = self.simulation.export_csv(dateiname) 659 | messagebox.showinfo("Export erfolgreich", f"Daten exportiert nach:\n{export_datei}") 660 | except Exception as e: 661 | messagebox.showerror("Export Fehler", f"Fehler beim Export: {str(e)}") 662 | 663 | def export_png(self): 664 | """Exportiert das Diagramm als PNG (vereinfacht)""" 665 | try: 666 | messagebox.showinfo("PNG Export", 667 | "PNG Export wird in dieser Version vereinfacht dargestellt.\n" 668 | "Die Daten sind im CSV-Export enthalten.") 669 | except Exception as e: 670 | messagebox.showerror("Export Fehler", f"Fehler beim PNG-Export: {str(e)}") 671 | 672 | def main(): 673 | """Hauptfunktion""" 674 | try: 675 | root = tk.Tk() 676 | app = PrimatenGUI(root) 677 | root.mainloop() 678 | except Exception as e: 679 | print(f"Fehler: {e}") 680 | input("Drücken Sie Enter zum Beenden...") 681 | 682 | if __name__ == "__main__": 683 | main() --------------------------------------------------------------------------------